设计模式——迭代器模式

240 阅读4分钟

什么是迭代器模式

迭代器模式的定义:提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。

就好比如,你每天乘坐公交地铁,都是按顺序一个个排好队来,不需要关心你的身份,是男是女,还是其他...

类图:

类图.png

场景

假设有这样一个场景:书架上有很多的书,这时候需要将名字按顺序显示出来;我们暂时不考虑java提供已有的api,我们程序应该如何去实现呢?大家可以一起想想

迭代器的主要角色

1. 迭代器(Iterator)

在示例中,Iterator代表了迭代器的角色,主要是提供了api接口,存在两个api接口,分别是hasNext作用在于是否存在下一个元素,next方法作用在于获取元素。

public interface Iterator {


    /**
     * 是否拥有下一个元素
     * @return
     */
    boolean hasNext();

    /**
     * 下一个元素
     * @return
     */
    Object next();

}

2. 具体迭代器(Concretelterator)

在示例中,BookShelfIterator表示具体迭代器角色,它实现了Iterator,该角色中存在需要遍历的bookShelf(书架),以及一个下标的字段,该字段要作用是为了标注遍历的位置。

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.getAt(index);
        index++;
        return book;
    }
}

3. 集合(Aggregate)

示例中,Aggregate表示集合,读者可以暂且把它理解为List,里面有一个iterator迭代方法,返回值为实现Iterator接口的实例,即具体迭代器。

public interface Aggregate {

    /**
     * 集合的迭代器
     * todo:思考:它的作用是什么,为什么要这么做
     * @return
     */
    Iterator iterator();
}

4. 具体的集合(ConcreteAggregate)

示例中,BookShelf表示具体的集合,它实现了Aggregate,该角色实现了iterator迭代方法。

public class BookShelf implements Aggregate{

    private Book[] books;

    private int lastIndex = 0;

    public BookShelf(int maxSize){
        this.books = new Book[maxSize];
    }

    public Book getAt(int index){
        return books[index];
    }

    public void appendBook(Book book){
        this.books[lastIndex] = book;
        lastIndex++;
    }

    public int getLength(){
        return lastIndex;
    }

    @Override
    public Iterator iterator() {
        return new BookShelfIterator(this);
    }
}

其他代码:

实体类

public class Book {

    private String name;

    private String price;
    
    //set get....
}

mian方法测试:

public class Main {

    public static void main(String[] args) {
        BookShelf bookShelf = new BookShelf(4);
        bookShelf.appendBook(new Book(){{setName("水浒传");}});
        bookShelf.appendBook(new Book(){{setName("西游记");}});
        bookShelf.appendBook(new Book(){{setName("红楼梦");}});
        bookShelf.appendBook(new Book(){{setName("三国演义");}});
        Iterator it = bookShelf.iterator();

        while (it.hasNext()){
            System.out.println(it.next().toString());
        }
    }
}

看完上述代码之后,我们再理解一下

首先该模式有四大角色:两个接口,两个实现,且Aggregate接口使用了Iterator接口,那么问题就来了,为啥要使用,那我们接着看实现Aggregate的实现,即BookShelf(书架),遍历书架上的书,自然在书架上进行遍历,所以不难解释为啥Aggregate接口使用了Iterator接口。迭代的实现交给了BookShelfIterator,将BookShelf(书架)赋值给BookShelfIterator中的bookShelf字段,BookShelfIterator通过实现的hasNext与next方法,对BookShelf进行依次迭代的一个过程。

序列类型的数据结构的遍历行为与被遍历的对象分离,即我们无需关心该序列的底层结构是什么样子的(这里的底层结构是指容器结构如数组,我们的示例中books就是数组对象)。只要拿到这个对象(指BookShelf),使用迭代器就可以遍历这个对象的内部。

思考点:

1. 增强for循环与迭代器

  1. 增强for用来处理集合中的每个元素而不用考虑集合定下标。简化了 Iterator 的书写。
  2. 增强for循环和iterator遍历的效果是一样的,也就说增强for循环的内部也就是调用iteratoer实现的,但是增强for循环有些缺点,例如不能在增强循环里动态的删除集合内容、不能获取下标等。

2. 什么场景需要用到

在日常开发中,我们很少写自己的迭代器。除非需要定制一个自己实现的数据结构对应的迭代器,否则,开源框架提供的 API 完全够用。

3. 为什么要用如此多的接口

解耦,具体类虽然可以解决我们的问题,但是难以被再次利用,为了弱化类与类之间的耦合关系,我们需要要引入大量的抽象类和接口,另外就是抽象思维很重要,能够让代码可维护性以及扩展性得到增强,不理解没关系,我们时时记着。其实我也不太理解,很多书都这样说...

参考资料《图解设计模式》