本文主要介绍迭代器模式原理和实现。
模式背景
对于一个list型数据,一般都会承担2个职责:存储数据,遍历数据。我们系统一般都会存在一些类,这些类的主要作用就是内部有个聚合对象(就是一堆对象,可能是list,set,map各种形式),对外我们可以存储和访问这些数据。正常思路上,这很简单,就把数据的存储和遍历方法都写在一个类里面就行了嘛!但是这种写法存在很多的缺陷:
- 这个聚合类职责过重,既负责存储,又负责遍历,可能会导致类很庞大。
- 我们存储数据和访问数据提供的方法五花八门,没法让客户端按照一个统一的方式来进行使用。(这个可以使用抽象来解决,但是如果所有的方法都抽象了,抽象类中的方法也挺多的,不利于子类实现)
- 我们设想一下,很多聚合类,他们存储是一样的,但是遍历不一定是一样的,所以遍历这个抽象一定会让子类自己实现,那如果系统很多聚合类都是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个继承系统:聚合类抽象体系负责数据的存储删除等操作,迭代器体系负责数据遍历读取的操作。
附
如有代码和文章问题,还请指正!感谢!