设计模式 | 迭代器模式的学习与思考

1. 什么是迭代器模式?

「官方定义:」 迭代器模式(Iterator),提供一种方法顺序访问一个聚合对象中的各种元素,但是又不暴露该对象的内部表示。(它是一种行为型模式)

「使用场景:」 1、访问一个聚合对象的内容而无须暴露它的内部表示。2、需要为聚合对象提供多种遍历方式。3、为遍历不同的聚合结构提供一个统一的接口

2. 迭代器模式的角色构成

  • 迭代器角色(Iterator):定义遍历元素所需要的方法,一般来说会有这么三个方法:取得下一个元素的方法next(),判断是否遍历结束的方法hasNext()),移出当前对象的方法remove(),

  • 具体迭代器角色(Concrete Iterator):实现迭代器接口中定义的方法,完成集合的迭代。

  • 容器角色(Aggregate):  一般是一个接口,提供一个iterator()方法,例如java中的Collection接口,List接口,Set接口等

  • 具体容器角色(ConcreteAggregate):就是抽象容器的具体实现类,比如List接口的有序列表实现ArrayList,List接口的链表实现LinkList,Set接口的哈希列表的实现HashSet等。

设计模式 | 迭代器模式的学习与思考

3. 迭代器模式在jdk中的应用

在jdk中,与迭代器相关的接口有两个:Iterator 与 Iterable

Iterator:迭代器,Iterator及其子类通常是迭代器本身的结构与方法;Iterable:可迭代的,那些想用到迭代器功能的其它类,如AbstractList HashMap等,需要实现该接口。

「Iterator接口的源码:」

public interface Iterator<E{    
    boolean hasNext();    
    next();    
    void remove();    
}    

「Iterable接口的源码」

public interface Iterable<T{    
    Iterator<T> iterator();    
}  

「迭代器模式的运转思路:」

  1. 若类A想要使用迭代器,则它的类声明部分为 class A implement Iterable
  2. 在类A实现中,要实现Iterable接口中的唯一方法:Iteratoriterator(); 这个方法用于返回一个迭代器,即Iterator接口及其子类;
  3. 在类A中,定义一个内部类S,专门用于实现Iterator接口,定制类A自已的迭代器实现。

4. 迭代器模式的实践

学习完迭代器模式,然后我就写了一个示例程序,示例程序的功能是将书放置到书架中,并将书的名字按顺序显示出来。

4.1 示例程序的类图

设计模式 | 迭代器模式的学习与思考

4.2 类与接口的说明

名字 说明
Aggregate 表示集合的接口
Iterator 遍历集合的接口
Book 表示书的类
BookShelf 表示书架的类
BookShelfIterator 遍历书架的类
Main 测试程序行为的类

4.3 代码设计

「Aggregate接口」

public interface Aggregate{
    public abstract Iterator iterator();
}

在Aggregate接口中声明的方法只有一个iterator方法,该方法会生成一个用于遍历集合的迭代器。想要遍历集合中的元素时,可以调用iterator生成一个实现了Iterator接口的类的实例。

「Iterator接口」

public interface Iterator{
    public abstract boolean hasNext();
    public abstract Object next();
}

这里的next方法既要能正确返回下一个元素,还要让迭代器移到下一个元素,当然Iterator接口只能只能看到方法名,要想知道next方法的逻辑还是要看看接口的实现类。

  • 「易错点:」 一般情况下next方法是返回当前元素,并指向下一个元素。
  • 「易错点:」 hasNext方法在返回最后一个元素前会返回true,在返回最后一个元素后会返回false。

「Book类」

public class Book{
    private String name;
    public Book(String name){
        this.name  = name;
    }
    public String getName(){
        return name;
    }
}

「BookShelf类」

public class BookShelf implements Aggregate{
    private Book[] books;
    private int last = 0;
    public BookShelf(int maxsize){
        this.books = new Book[maxsize];
    }
    public Book getBookAt(int index){
        return books[index];
    }
    public void appendBook(Book book){
        this.books[last] = book;
        last++;
    }
    public int getLength(){
        return last;
    }
    @Override
    public Iterator iterator(){
        return new BookShelfIterator(this);
    }
}

书架类定义了一个Book类型的数组,它实现了Aggregate接口,覆写了iterator方法。该方法生成并返回了BookShelfIterator类的实例作为BookShelf类对应的Iterator。当外部想要便利书架时,就会调用这个方法。

「BookShelfIterator类」

public class BookShelfIterator implements Iterator{
    private BookShelf bookShelf;
    private int index;
    public BookShelfIterator(BookShelf bookShelf){
        this.bookShelf = bookShelf;
        this.index = 0;
    }
    @Override
    public boolean hasNext() {
        if(index < bookShelf.getLength()){
            return true;
        }else{
            return false;
        }
    }

    @Override
    public Object next() {
        Book book = bookShelf.getBookAt(index);
        index++;
        return book;

    }
}

bookShelf字段表示BookShelfIterator所要遍历的书架,index字段表示迭代器当前所指向书的下标。构造函数会将接收到的BookShelf实例保存到bookShelf字段中,并将index初始化为0。hasNext方法将会判断书架中还有没有下一本书。next方法会返回迭代器当前所指向的书籍,并让迭代器指向下一本书。

「Main类」

public class Main {
    public static void main(String[] args) {
        BookShelf bookShelf = new BookShelf(4);
        bookShelf.appendBook(new Book("三国演义"));
        bookShelf.appendBook(new Book("红楼梦"));
        bookShelf.appendBook(new Book("西游记"));
        bookShelf.appendBook(new Book("水浒传"));
        Iterator iterator = bookShelf.iterator();
        while(iterator.hasNext()){
            Book book = (Book) iterator.next();
            System.out.printf(book.getName());
        }
    }
}
设计模式 | 迭代器模式的学习与思考

5. 我的理解

迭代器模式与java容器密不可分,他要把遍历算法从容器对象中独立出来。那么为什么要把遍历算从容器对象中独立出来呢? 因为在面向对象设计中,一个难点就是辨认对象的职责。理想的状态下,一个类应该只有一个单一的职责。职责分离可以最大限度的去解耦,但是职责单一说起来容易,做起来难。具体到本模式,我们明显可以看到,一个容器对象它提供了两个职责:一是组织管理数据对象,二是提供遍历算法。所以以Iterator模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明的访问集合内部的数据。

「优点:」  1、它支持以不同的方式遍历一个聚合对象。2、迭代器简化了聚合类。3、在同一个聚合上可以有多个遍历。4、在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码。

「缺点:」 由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。

原文始发于微信公众号(Java之禅):设计模式 | 迭代器模式的学习与思考

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/161381.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!