设计模式——迭代器模式

207 阅读4分钟

一、概述

迭代器模式提供了一种方式去访问一个容器元素中的各个对象,而又不暴露该对象的内部细节,它是一种行为型的设计模式。迭代器就是为对集合类(聚合类)进行循环遍历,但是集合类已经存储了元素还要进行遍历的话,这就不符合单一职责原则。其实说白了,迭代器就是帮助集合类进行遍历的。

迭代器需要有4个核心结构:

  • 抽象的迭代器角色:负责定义访问和遍历元素的接口;
  • 具体的迭代器角色:实现迭代器接口,并要记录遍历中的当前位置;
  • 抽象的容器角色:负责提供创建具体迭代器角色的接口;
  • 具体的容器角色:实现创建具体迭代器角色的接口,这个具体的迭代器角色与该容器的结构相关;

迭代器在Java的集合框架中的典型应用,通过Iterator迭代器来遍历集合,ListIterator是专门为遍历List而存在的。所以正是因为有了Java帮我们封装了的迭代器,我们在使用它时就比较简单,它也不太受到关注,平时就更少自己实现迭代器模式了。下面我们实现一个简单的Iterator迭代器和List容器。

二、使用

首先我们创建抽象的迭代器角色,它只负责两件事:访问元素和遍历元素,所以它只需要2个方法。

/**
 * 自定义的抽象迭代器接口
 */
public interface MyIterator {

    //返回下一个对象
    Object next();

    //是否还有下一个元素
    boolean hasNext();
}

然后我们创建抽象的容器角色,它就是所有容器的高度抽象的接口,它依赖于我们上面创建的迭代器接口,我们这里只添加3个方法:添加元素、获取容器长度、获取迭代器。

/**
 * 抽象的容器接口
 */
public interface MyCollection {

    //添加元素
    void add(Object o);

    //容器大小
    int size();

    //迭代方法
    MyIterator iterator();
}

再者我们创建具体的迭代器角色,其实就是需要在具体的逻辑中实现接口中的方法,并且要记录当前遍历的位置。hasNext方法判断很简单,只需要判断当前记录的位置是否大于了容器的长度;next方法需要获取容器中的元素,所以迭代器需要持有一个容器角色的引用。

/**
 * 具体的迭代器
 */
public class MyArrayListIterator implements MyIterator{

    //记录当前位置
    private int currentIndex = 0;
    private MyArrayList myArrayList;

    public MyArrayListIterator(MyArrayList myArrayList){
        this.myArrayList = myArrayList;
    }

    @Override
    public Object next() {
        Object o = null;
        if(hasNext()){
            o = myArrayList.get(currentIndex);
        }
        currentIndex++;
        return o;
    }

    @Override
    public boolean hasNext() {
        return currentIndex < myArrayList.size();
    }
}

最好就是创建具体的容器角色,代码中我们只是简单模拟了Java中的ArrayList类,有添加、获取、扩容等操作,都很简单就不再过多说明。需要注意的是iterator()方法,我们直接返回了MyArrayListIterator。

/**
 * 具体的容器类
 */
public class MyArrayList implements MyCollection{

    //List成员
    private Object[] elementData;

    //List长度
    private int size;

    public MyArrayList(int size) {
        super();
        this.elementData = new Object[size];
        this.size = 0;
    }

    public MyArrayList(){
        this(10);
    }

    @Override
    public void add(Object o) {
        if(this.size >= 10){
            int newSize = size * 3;
            elementData = Arrays.copyOf(elementData, newSize);
        }
        elementData[size] = o;
        size++;
    }

    public Object get(int size){
        return elementData[size];
    }

    @Override
    public int size() {
        return size;
    }

    @Override
    public MyIterator iterator() {
        return new MyArrayListIterator(this);
    }
}

下面来测试一下。打印输出结果如下图。

MyArrayList arrayList = new MyArrayList();
arrayList.add("aaa");
arrayList.add("bbb");
arrayList.add("ccc");
arrayList.add("ddd");
MyArrayListIterator arrayListIterator = new MyArrayListIterator(arrayList);
while (arrayListIterator.hasNext()){
    Log.e(TAG, "数据有:" + arrayListIterator.next());
}

代码也有需要改进的地方,比如MyArrayList可以改成泛型类,而不是直接用一个Object代替,这里只是为了说明迭代器的相关使用,可以自己下来改一下。

三、总结

迭代器模式使用场景不多,除了能在java集合框架中遍历的同时,能对元素进行修改(添加、删除等)的这个地方使用最多。从上面的例子中我们也可以看到迭代器也有一些优点:

  • 封装性良好,用户只需要得到迭代器就可以遍历,而对于遍历算法则不用去关心;
  • 可以提供多种遍历方式,比如说对有序列表,我们可以根据需要提供正序、倒序遍历两种迭代器;
  • 客户端可以通过相同的方式遍历不同的容器对象;
  • 由于引入了抽象迭代器,所以我们进行扩展的时候会比较方便,无需修改源码,符合开闭原则;但这也可能会造成前期设计考虑不够完善,后面修改源码时所有的具体迭代器都会受到影响。

使用迭代器可能就没有直接用for循环遍历方便,在使用上稍微繁琐一些,这个就根据自己的情况来了。

github地址:github.com/leewell5717…

四、参考

【JAVA】设计模式之迭代器(Iterator)模式的使用分析

Java中的迭代器模式