设计模式-01 迭代器模式
设计模式-01 迭代器模式
目录- 设计模式-01 迭代器模式
- 简介
- 例子
- 类图
- 代码
- 分析
- 特点
- 总结
- 通用结构
- 屏蔽实现
- 降低耦合
- 灵活迭代
简介
Iterator 中文对应迭代器,用于在数据集合中按照顺序遍历集合。
举一个最基本的遍历数组的例子:
public void MyStorage {
private int[] arr;
// ...
public void iterateArr() {
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
类中的 iterate 方法非常完美地解决了遍历的任务,小巧精干。
但是:
- 它的实现决定了它只能遍历数组。
- 暴露细节过多,如果要更改数据存储结构(arr 改成 List 类型了),则需要修改而不是添加代码,违反 OCP 原则。
- 这个方法告诉和保证给调用者的细节过多(数据存储类型),不符合面向抽象而非具体编程的原则。
- ……
为了解决这些问题,我们引入 Iterator 模式。
例子
类图
代码
public interface Aggregate {
Iterator iterator();
}
public interface Iterator {
boolean hasNext();
Object next();
}
public class BookShelf implements Aggregate {
private Book[] books;
private int last;
public BookShelf(int maxsize) {
this.books = new Book[maxsize];
}
public Book getBookAt(int index) {
return books[index];
}
public void appendBook(Book book) {
this.books[last] = book;
last++;
}
public int getLength() {
return last;
}
@Override
public Iterator iterator() {
return new BookShelfIterator(this);
}
}
public class BookShelfIterator implements Iterator {
private BookShelf bookShelf;
private int index;
public BookShelfIterator(BookShelf bookShelf) {
this.bookShelf = bookShelf;
this.index = 0;
}
@Override
public boolean hasNext() {
if (index < bookShelf.getLength()) {
return true;
} else {
return false;
}
}
@Override
public Object next() {
Book book = bookShelf.getBookAt(index);
index++;
return book;
}
}
public class Book {
private String name;
public Book(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
'}';
}
}
分析
特点
- Aggregate 接口规定了它的实现类必须具有返回一个 Iterator 的方法,也就是说它的实现者能生成迭代器进行迭代。
- Iterator 接口规定了它的实现类必须要有 next 和 hasNext 两个方法,也就是说,所有的迭代器必须要实现这两个功能。
- BookShelfIterator 类实现了 Iterator 接口,表明它是一个迭代器,而且专门迭代 BookShelf。
- BookShelf 类实现了 Aggregate 接口,所以它把自己作为参数,初始化返回了 BookShelfIterator 。
Client 要想遍历 BookShelf,只需要调用后者的 iterator 方法,获得 BookShef 的迭代器,然后使用迭代器进行迭代。不止对 BookShelf 这样进行迭代操作,用户对凡是实现了 Aggregate 接口的集合进行迭代,都是如此。在这个过程中:
- 迭代的过程和具体方式对 Client 来说是未知的,Client 用一个 Iterator 引用的实例进行迭代,非常符合面向抽象(接口)的原则。
- 假如 BookShelf 改用
ArrayList实现书本的存储,只需要增加一个新的、实现了 Iterator 接口的迭代器,并且在这个迭代器中编写遍历 ArrayList 的具体实现,最后在 BookShelf 的 iterator方法中返回新的迭代器即可,符合 OCP 原则。 - Client 完全感知不到数据存储结构和遍历实现的改变,如果在新的迭代器实现中改成逆序迭代,Client 也只会感知到迭代出的元素顺序发生了改变,完全不清楚具体的细节。
总结
通用结构
这是更为一般的迭代器模式:
回到一个设计模式共性的问题:为什么这么麻烦?既然是一个数组,直接遍历处理就是最好的操作,似乎根本没有引入 Iterator 的必要。
核心问题:替换存储结构而完全不影响程序的其它部分。
屏蔽实现
如果 BookShelf 改用 java.util.List 管理书本,Client 使用 Iterator 进行迭代的操作代码不需要任何变动:
while (iterator.hasNext()) {
Book book = (Book) iterator.next();
System.out.println(book.getName())
}
设计模式的作用之一就是帮助我们编写可复用的类,而所谓的可复用就是当一个组件(类)发生变化时,不需要对其他组件进行改动,或者是进行很小的改动就能应对新的变化和需求。这也进一步解释了,在 BookShelf 的 iterator 方法中返回的是一个 Iterator 而不是 BookShelfIterator。
降低耦合
以往,我们更喜欢使用具体的类来解决具体的问题,这是一种务实的做法,但具体类的编写往往会造成强的关联和耦合,设计模式的任务之一,就是要降低组件或者说代码之间的耦合。
灵活迭代
一个 ConcreteAggregate 可以对应多个 ConcreteIterator,可以从前往后迭代,也可以从后往前迭代……