行为型模式
目录
1、迭代器模式
迭代器模式(Iterator),提供一种方法顺序访问一个聚合对象中的各种元素,而又不暴露该对象的内部表示。
迭代器模式是针对集合对象而生的,对于集合对象而言,肯定会涉及到对集合的添加和删除操作,同时也肯定支持遍历集合元素的操作,我们此时可以把遍历操作放在集合对象中,但这样的话,集合对象既承担太多的责任了,面向对象设计原则中有一条就是单一职责原则,所有我们要尽可能地分离这些职责,用不同的类取承担不同的责任,迭代器模式就是用迭代器类来承担遍历集合的职责。
- 意图:提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。
- 主要解决:不同的方式来遍历整个整合对象。
- 何时使用:遍历一个聚合对象。
- 如何解决:把在元素之间游走的责任交给迭代器,而不是聚合对象。
- 关键代码:定义接口:hasNext, next。
1.1 迭代器模式UML图

1.2 日常生活中看迭代器模式与应用实例
迭代器模式在生活中应用的比较广泛,比如:物流系统中的传送带,不管传送的是什么物品,都会被打包成一个个箱子,并且有一个统一的二维码。这样我们不需要关心箱子里是什么,在分发时只需要一个个检查发送的目的地即可。再比如,我们平时乘坐交通工具,都是统一刷卡或者刷脸进站,而不需要关心是男性还是女性、是残疾人还是正常人等信息。
1.3 Java代码实现
将书(Book)放到书架(BookShelf)中,并将书名按顺序显示。
Aggregate 接口
所要便利的集合的接口。实现了该接口的类将成为一个可以保存多个元素的集合,类似数组。
public interface Aggregate{
public abstract Iterator iterator();
}
Aggregate接口中声明的方法为iterator,作用为生成一个用于遍历的迭代器。
Iterator 接口
作用为遍历集合中元素,相当于循环语句中的循环变量(for(int i =0 ;i<arr.lenth;i++
),具体实现一个顺序遍历的迭代器。
public interface Iterator{
public abstract boolean hasNext();
public abstract Object next();
}
hasNext() 方法判断是否存在下一个,next()方法获取下一个元素。
特殊说明下,next方法在获取元素的同时,要将计数器向下一个元素的计数加一。获取的是当前元素,并指向下一个元素。
Book类
普通类,书名field 获取书名的getName()方法。构造函数初始化书名。
public class Book{
private String name ;
public Book(String name){
this.name=name;
}
public String getName(){
return name;
}
}
BookShelf 类
书架类,作为存放书的集合类,实现Aggregate接口。实现了Aggregate接口的iterator方法。
public class BookShelf implements Aggregate {
private List<Book> books;
public BookShelf() {
this.books = new ArrayList<Book>();
}
public Book getBookAt(int index) {
return books.get(index);
}
public void appendBook(Book book) {
books.add(book);
}
public int getLength() {
return books.size();
}
public Iterator iterator() {
return new BookShelfIterator(this);
}
}
主要点在iterator方法,方法返回了遍历书架时要用的BookShelfIterator类作为书架的迭代器。当外部要遍历书架时会调用该方法。
BookShelfIterator类
public class BookShelfIterator implements Iterator {
private BookShelf bookShelf;
private int index;
public BookShelfIterator(BookShelf bookShelf) {
this.bookShelf = bookShelf;
this.index = 0;
}
public boolean hasNext() {
if (index < bookShelf.getLength()) {
return true;
} else {
return false;
}
}
public Object next() {
Book book = bookShelf.getBookAt(index);
index++;
return book;
}
}
作为一个迭代器,要实现Iterator接口。index为迭代器当前所指向的下标。
hasNext判断还有没有下一本。通过下标和总数比较判断。
next获取当前书,并指向下一个。
Main类
public class Main {
public static void main(String[] args) {
BookShelf bookShelf = new BookShelf();
bookShelf.appendBook(new Book("Around the World in 80 Days"));
bookShelf.appendBook(new Book("Bible"));
bookShelf.appendBook(new Book("Cinderella"));
bookShelf.appendBook(new Book("Daddy-Long-Legs"));
Iterator it = bookShelf.iterator();
while (it.hasNext()) {
Book book = (Book) it.next();
System.out.println(book.getName());
}
}
}
控制台:
----------------------------------
Around the World in 80 Days
Bible
Cinderella
Daddy-Long-Legs
----------------------------------
Iterator模式中各角色的作用
Iterator(迭代器)
该角色责任定义按顺序逐个遍历元素的接口。
程序中,由Iterator接口扮演,定义了hasNext和next两个方法。
Concretelterator(具体的迭代器)
该角色负责实现Iterator角色所定义的接口.该角色包含了遍历集合所必须的信息。
Aggregate(集合)
该角色负责定义创建Iterator角色的接口。这个接口是一个方法会创建出一个,按照顺序访问保存在我内部元素的人。
ConcreteAggregate(具体集合)
该角色负责实现Aggregate角色所定义的接口。他会创建出具体的Iterator角色,也就是ConcreteIterator,也就是实例中的BookShelf。
Iterator 模式的类图

2、迭代器模式在源码中的应用
2.1 JDK源码中迭代器模式体现
以下基于JDK8.0
在JDK中最能体现迭代器模式的地方莫过于JDK中的容器类了,首先有一个Iterator接口,该接口包含了迭代过程中需要用到的几个方法,最重要的两个方法是hasNext()和next();简化之后的代码如下所示:
public interface Iterator<E> {
boolean hasNext();
E next();
}
以ArrayList为例,看一下它的继承层次:ArrayList实现了List接口,List接口继承了Collection接口,Collection接口继承了Iterable接口,Iterable接口中包含了一个iterator()方法,因此到ArrayList中就需要实现该iterator方法,该方法的实现很简单,就是返回一个实现了Iterator接口的迭代器实例。ArrayList中迭代器的实现采用的是内部类的形式,由于内部类可以直接访问外部类的成员变量,所以该迭代器内部类可以很方便地实现Iterator接口中的hasNext()和next()方法,ArrayList中的迭代器内部类名字是Itr,简化代码如下所示:
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
public boolean hasNext() {
return cursor != size;
}
public E next() {
Object[] elementData = ArrayList.this.elementData;
return (E) elementData[cursor++];
}
}
这是ArrayList中的一个内部类,该类实现了Iterator接口,在实现hasNext()和next()方法的时候可以很方便地访问ArrayList的成员变量size和elementData数组。
ArrayList还有一个public成员方法iterator()该方法就可以直接返回一个该内部类迭代器的实例,其代码如下
public Iterator<E> iterator() {
return new Itr();
}
得到迭代器实例之后就可以用该迭代器实例对集合进行迭代了。代码如下所示:
ArrayList list = new ArrayList();
for(int i=0; i<5; i++) {
list.add(i);
}
Iterator iter = list.iterator();
while(iter.hasNext()) {
System.out.println(iter.next());
}
迭代器模式让我们在遍历集合元素的时候无需了解集合内部的实现形式,比如说有另外一种集合类LinkedList,它的底层是用链表实现的,只要它也同样提供一个返回实现了Iterator接口的迭代器实例的方法,我们就可以用统一的方式对集合进行遍历。JDK中有无尽的宝藏,值得我们不断探索。水平有限,如有不当之处,还望指正!
2.2 MyBatis源码中迭代器模式体现
MyBatis的源码包cursor就是迭代器模式的应用,下面是缓存包的包结构:
从上面的包结构图结合具体的代码可知内容如下:
下面来看 DefaultCursor 类,源码如下。
public class DefaultCursor<T> implements Cursor<T> {
...
private final CursorIterator cursorIterator = new CursorIterator();
...
}
DefaultCursor 实现了 Cursor 接口,且定义了一个成员变量 cursorIterator,其类型为 CursorIterator。
继续查看 CursorIterator 类的源码实现,它是 DefaultCursor 的一个内部类,实现了 JDK 中的 Iterator 接口,源码如下。
private class CursorIterator implements Iterator<T> {
T object;
int iteratorIndex;
private CursorIterator() {
this.iteratorIndex = -1;
}
public boolean hasNext() {
if (this.object == null) {
this.object = DefaultCursor.this.fetchNextUsingRowBound();
}
return this.object != null;
}
public T next() {
T next = this.object;
if (next == null) {
next = DefaultCursor.this.fetchNextUsingRowBound();
}
if (next != null) {
this.object = null;
++this.iteratorIndex;
return next;
} else {
throw new NoSuchElementException();
}
}
public void remove() {
throw new UnsupportedOperationException("Cannot remove element from Cursor");
}
}
3、迭代器模式优缺点
3.1 优点
- 它支持以不同的方式遍历一个聚合对象。
- 迭代器简化了聚合类。
- 在同一个聚合上可以有多个遍历。
- 在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码。
3.2 缺点
增加了类的个数,这在一定程度上增加了系统的复杂性。
3.3 使用场景
- 访问一个聚合对象的内容而无须暴露它的内部表示。
- 需要为聚合对象提供多种遍历方式。
- 为遍历不同的聚合结构提供一个统一的接口。
3.4 注意事项
迭代器模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明地访问集合内部的数据。
4、总结
迭代器模式常常与组合模式结合起来使用,在对组合模式中的容器构件进行访问时,经常将迭代器潜藏在组合模式的容器构成类中。当然,也可以构造一个外部迭代器来对容器构件进行访问,其结构图如图
参考文章:
https://blog.youkuaiyun.com/abinge317/article/details/51313769
https://blog.youkuaiyun.com/jian_j_z/article/details/80256405?utm_source=blogkpcl8
https://www.jianshu.com/p/3dd7b4e73561