【设计模式】迭代器模式

180 阅读5分钟

本文主要介绍迭代器模式原理和实现。

模式背景

对于一个list型数据,一般都会承担2个职责:存储数据遍历数据。我们系统一般都会存在一些类,这些类的主要作用就是内部有个聚合对象(就是一堆对象,可能是list,set,map各种形式),对外我们可以存储和访问这些数据。正常思路上,这很简单,就把数据的存储和遍历方法都写在一个类里面就行了嘛!但是这种写法存在很多的缺陷:

  1. 这个聚合类职责过重,既负责存储,又负责遍历,可能会导致类很庞大。
  2. 我们存储数据和访问数据提供的方法五花八门,没法让客户端按照一个统一的方式来进行使用。(这个可以使用抽象来解决,但是如果所有的方法都抽象了,抽象类中的方法也挺多的,不利于子类实现)
  3. 我们设想一下,很多聚合类,他们存储是一样的,但是遍历不一定是一样的,所以遍历这个抽象一定会让子类自己实现,那如果系统很多聚合类都是list,偶尔几个是其他的特殊类型,我们那些list的遍历代码是一样的,就有大量的代码重复。

所以迭代器模式的思路就是将数据的存储和遍历进行隔离解耦(本质奔着的目的还是因为这个聚合类的职责太重了)。我们认为数据的遍历是可以变化,可以分离的。

定义&概念

迭代器模式(Iterator):提供一种方法顺序访问一个聚合对象中的各种元素,而又不暴露该对象的内部表示。其别名为游标(Cursor)。迭代器模式是一种对象行为型模式。

原理

我们要将聚合类的数据存储和数据的遍历给隔离,将遍历数据的行为从聚合类中抽取出来。整个迭代器模式就分为了2个体系,这2个体系都有各自的一个抽象:聚合类的抽象迭代器的抽象。让抽象聚合类及其子类专门负责数据的存储等操作,抽象迭代器以及子类专门负责数据的遍历等操作。

组成要素

  • 抽象迭代器:定义遍历集合元素的接口,以及一些其他的访问集合元素的操作。
  • 具体迭代器:迭代器遍历的访问的具体逻辑实现。
  • 抽象集合类:申明一个创建迭代器方法的抽象类。用于存储和管理对象。
  • 具体聚合类:抽象集合类具体实现,主要返回一个针对该具体聚合类的具体迭代器对象。

因为迭代器和聚合类本身又具有整体性,我们有时候也会将迭代器以内部类的形式放到聚合类里面。JDK的迭代器实现其实就是使用的内部类的方式。

不管迭代器是外部,还是内部,客户端访问使用的方式都是一致的。客户端只需要通过抽象聚合操作数据存储,通过迭代器进行遍历数据。

UML

实现

抽象聚合类

public abstract class AbstractObjectList {

    protected List<Object> objects = new ArrayList<>();

    public AbstractObjectList(List objects) {
        this.objects = objects;
    }

    public void addObject(Object object) {
        this.objects.add(object);
    }

    public void removeObject(Object object) {
        this.objects.remove(object);
    }

    public List<Object> getObjects() {
        return objects;
    }

    /**
     * 创建一个迭代器
     */
    public abstract AbstractIterator createIterator();
}

具体集合类

public class ConcreteList extends AbstractObjectList {

    public ConcreteList(List objects) {
        super(objects);
    }

    @Override
    public AbstractIterator createIterator() {
        return new ConcreteIterator(this);
    }
}

迭代器抽象

interface AbstractIterator {
    void next();

    boolean isLast();

    void previous();

    boolean isFirst();

    Object getNextItem();

    Object getPreviousItem();
}

具体迭代器

public class ConcreteIterator implements AbstractIterator {

    /**
     * 引用被迭代的数据
     */
    private ConcreteList concreteList;
    private List data;

    /**
     * 开始位置 往后移动的标志
     */
    private int cursor1;
    /**
     * 结束位置 往前移动的标志
     */
    private int cursor2;

    public ConcreteIterator(ConcreteList concreteList) {
        this.concreteList = concreteList;
        this.data = concreteList.getObjects();
        cursor1 = 0;
        cursor2 = data.size() - 1;
    }

    @Override
    public void next() {
        if (cursor1 < data.size()) {
            cursor1++;
        }
    }

    @Override
    public boolean isLast() {
        return cursor1 == data.size();
    }

    @Override
    public void previous() {
        if (cursor2 > -1) {
            cursor2--;
        }
    }

    @Override
    public boolean isFirst() {
        return cursor2 == -1;
    }

    @Override
    public Object getNextItem() {
        return data.get(cursor1);
    }

    @Override
    public Object getPreviousItem() {
        return data.get(cursor2);
    }
}

使用

List data = new ArrayList();
data.add("A");
data.add("B");
data.add("C");
data.add("D");
data.add("E");
data.add("F");

ConcreteList concreteList = new ConcreteList(data);
AbstractIterator iterator = concreteList.createIterator();
while (!iterator.isLast()) {
    System.out.println(iterator.getNextItem());
    iterator.next();
}

内部类迭代器

可以将迭代器放入到具体实现类中,作为一个内部类。JDK就是这么玩的。

public class InnerClassIteratorList extends AbstractObjectList {
    public InnerClassIteratorList(List objects) {
        super(objects);
    }

    @Override
    public AbstractIterator createIterator() {
        return new InnerIterator();
    }

    private class InnerIterator implements AbstractIterator {
        private int cursor1;
        private int cursor2;

        public InnerIterator() {
            cursor1 = 0;
            cursor2 = objects.size() - 1;
        }

        @Override
        public void next() {
            if (cursor1 < objects.size()) {
                cursor1++;
            }
        }

        @Override
        public boolean isLast() {
            return cursor1 == objects.size();
        }

        @Override
        public void previous() {
            if (cursor2 > -1) {
                cursor2--;
            }
        }

        @Override
        public boolean isFirst() {
            return cursor2 == -1;
        }

        @Override
        public Object getNextItem() {
            return objects.get(cursor1);
        }

        @Override
        public Object getPreviousItem() {
            return objects.get(cursor2);
        }
    }
}

优缺点

优点

  • 可以使用不同的方式遍历一个聚合对象,只需要扩展迭代器抽象就行了
  • 简化了聚合类
  • 迭代器和聚合类都有抽象层,很方便扩展。

缺点

  • 一定程度增加了系统的复杂度

使用场景

  • 需要访问一个聚合对象,但又不想暴露其内部实现细节的情况。
  • 一个聚合对象需要多种遍历方式,后面可能需要动态变化其遍历方式。

总结

主要目的就是将对集合的遍历操作从类中抽取独立出来。形成2个继承系统:聚合类抽象体系负责数据的存储删除等操作,迭代器体系负责数据遍历读取的操作。

相关代码:github.com/zhaohaoren/…

如有代码和文章问题,还请指正!感谢!