设计模式--迭代器模式
简介
迭代器模式(Iterator Pattern)又称为游标模式(Cursor Pattern),它提供了一种顺序访问集合/容器对象元素的方法,而又无需暴露集合内部表示。迭代器模式可以为不同的容器提供一致的遍历行为,而不用关心容器内容元素组成结构,属于行为型模式。
迭代器模式的本质是抽离集合对象迭代行为到迭代器中,提供一致访问接口。
迭代器模式的应用场景
1、物流系统的传送带,不管传送的是什么 都打包成一个统一的箱子。
2、或者刷脸或刷卡进站,不同的人,统一的工具
我们把多个对象聚在一起形成的总体称之为集合(Aggregate),集合对象是能包容一组对象的容器对象。不同的集合其内部元素的聚合结构可能不同,而迭代器屏蔽了内部元素获取细节,为外部提供一致的元素访问行为,解耦了元素迭代与集合对象间的耦合,并且通过提供不同的迭代器,可以为同个集合对象提供不同顺序的元素访问行为,扩展了集合对象元素迭代功能,符合开闭原则。
迭代器适用于以下场景:
1、访问一个集合对象的内容而无需暴露它的内部表示;
2、为遍历不同的集合结构提供一个统一的访问接口。
迭代器模式的通用UML类图:
从UML类图中,可以看到,迭代器模式主要包含三种角色:
抽象迭代器(Iterator):抽象迭代器负责定义访问和遍历元素的接口;
具体迭代器(ConcreteIterator):提供具体的元素遍历行为;
抽象容器(Aggregate):负责定义提供具体迭代器的接口;
具体容器(ConcreteAggregate):创建具体迭代器。
迭代器模式在源码中的使用
1、JDK中的Iterator源码
public interface Iterator<E> {
boolean hasNext();
E next();
default void remove() {
throw new UnsupportedOperationException("remove");
}
default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
}
从上面的代码中,我们可以看到remove()方法,这个在组合模式中也见过。迭代器模式和组合模式,两者似乎存在一定的相似性。组合模式解决的是统一树形结构各层次访问接口,迭代器模式解决的是统一各集合对象元素遍历接口。他们的适配场景不同,但是核心理念是想通的。
2、Iterator的实现类ArrayList中有一个内部实现类Itr,它实现了Iterator
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
...
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
Itr() {}
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
...
}
...
}
其中hasNext()方法和next()方法实现也非常简单,继续往下看ArrayList内部还有几个迭代器对Itr进行了进一步扩展。
- ListItr
private class ListItr extends Itr implements ListIterator<E> {
ListItr(int index) {
super();
cursor = index;
}
public boolean hasPrevious() {
return cursor != 0;
}
public int nextIndex() {
return cursor;
}
public int previousIndex() {
return cursor - 1;
}
...
}
他增加了hasPrevious()
方法是否还有上一个这样的判断。
3、Mybatis中的DefaultCursor类
public class DefaultCursor<T> implements Cursor<T> {
...
private final CursorIterator cursorIterator = new CursorIterator();
...
protected class CursorIterator implements Iterator<T> {
T object;
int iteratorIndex = -1;
@Override
public boolean hasNext() {
if (!objectWrapperResultHandler.fetched) {
object = fetchNextUsingRowBound();
}
return objectWrapperResultHandler.fetched;
}
@Override
public T next() {
// Fill next with object fetched from hasNext()
T next = object;
if (!objectWrapperResultHandler.fetched) {
next = fetchNextUsingRowBound();
}
if (objectWrapperResultHandler.fetched) {
objectWrapperResultHandler.fetched = false;
object = null;
iteratorIndex++;
return next;
}
throw new NoSuchElementException();
}
@Override
public void remove() {
throw new UnsupportedOperationException("Cannot remove element from Cursor");
}
}
}
首先它实现了Cursor接口,而且定义了一个成员变量cursorIterator,继续查看CursorIterator的源代码发现它是DefaultCursor的一个内部类,并且实现了JDK中的Iterator接口。
迭代器模式的优缺点
优点
1、多态迭代:为不同的聚合结构提供一致的遍历接口,即一个迭代接口可以访问不同的集合对象;
2、简化集合对象接口:迭代器模式将集合对象本身应该提供的元素迭代接口抽取到了迭代器中,使集合对象无须关心具体迭代行为;
3、元素迭代功能多样化:每个集合对象都可以提供一个或多个不同的迭代器,使得同种元素聚合结构可以有不同的迭代行为;
4、解耦迭代与集合:迭代器模式封装了具体的迭代算法,迭代算法的变化,不会影响到集合对象的架构。
缺点:
1、对于比较简单的遍历(像数组或者有序列表),使用迭代器方式较为繁琐。
结语:在日常开发中,我们几乎不会自己写迭代器。除非我们需要定制一个自己实现的数据结构对应的迭代器,否则,开源框架提供给我们的API完全够用。