一、迭代器(Iterator)模式要解决的问题
使用java语言显示数组中元素时,可以使用for循环遍历数组:
for(int i=0;i<arr.length;i++){
System.out.println(arr[i]);
}
通过数组的下标我们可以取出数组任意索引位置的值,所以通过i的不断自增,我们可以取出数组中的所有元素。将这里的循环变量i的作用抽象化、通用化后形成的模式,在设计模式中称为Iterator模式。该模式用于在数据集合中按照顺序遍历集合。
二、说明迭代器模式的示例程序
我们这里会定义一个书籍类(Book)和一个书架类(BookShelf),实现一个可以遍历书架上书籍的迭代器。
类图
类说明
类代码
1、Aggregate接口
该接口声明了一个iterator方法,该方法会生成一个用于遍历集合的迭代器。遍历集合时只要调用iterator方法生成一个实现了Iterator接口的类的实例。
package com.dsanjun.gof.iterator;
/**
* 表示集合类接口
*
* @author dyw
* @date 2019年10月5日
*/
public interface Aggregate {
/**
* 返回迭代器
*
* @return 迭代器
*/
public abstract Iterator iterator();
}
2、Iterator 接口
简单的Iterator接口包含两个方法,hasNext用于判断是否存在下一个元素,next用于取回下一个元素,需要注意的是next方法还需要包含将迭代器移动至下一个元素的功能。
package com.dsanjun.gof.iterator;
/**
* 遍历集合的迭代器接口
*
* @author dyw
* @date 2019年10月5日
*/
public interface Iterator {
/**
* 是否有下一个元素(用于终止循环)
*
* @return
*/
public abstract boolean hasNext();
/**
* 返回下一个元素
*
* @return
*/
public abstract Object next();
}
3、Book类
package com.dsanjun.gof.iterator;
/**
* 书籍类
*
* @author dyw
* @date 2019年10月5日
*/
public class Book {
private String name;
public Book(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Book [name=" + name + "]";
}
}
4、BookShelf类
该类实现了Aggregate接口的iterator方法,返回一个用于迭代BookShelf的Iterator实现类的一个实例。
package com.dsanjun.gof.iterator;
/**
* 书架类
*
* @author dyw
* @date 2019年10月5日
*/
public class BookShelf implements Aggregate {
/** 书的数组 */
private Book[] books;
/** 实际元素数量 */
private int size = 0;
/**
* 初始化一个数组
*
* @param maxsize 数组最大长度
*/
public BookShelf(int maxsize) {
this.books = new Book[maxsize];
}
/**
* 获取指定索引位置的元素,这个方法会在迭代器中使用
*
* @param index 索引位置
* @return Book 书籍对象
*/
public Book getBookAt(int index) {
return books[index];
}
/**
* 往数组末位添加元素
*
* @param Book 书籍对象
*/
public void appendBook(Book book) {
this.books[size] = book;
}
/**
* 获取元素个数(模拟集合的size),这个方法会在迭代器中使用
*
* @return 元素个数
*/
public int size() {
return size;
}
@Override
public Iterator iterator() {
return new BookShelfIterator(this);
}
}
5、BookShelfIterator类
该类适用于迭代BookShelf,由于需要发挥迭代功能,所以要实现Iterator接口的方法。构造函数初始化一个BookShelf实例保存到bookShelf域中,并将index初始化为0。hasNext用于判断是否还有下一个元素,next方法返回当前迭代所指向的书籍,并将迭代器指向下一本书。
package com.dsanjun.gof.iterator;
/**
* 书架对象的迭代器
*
* @author dyw
* @date 2019年10月5日
*/
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.size()) {
return true;
} else {
return false;
}
}
@Override
public Object next() {
Book book = bookShelf.getBookAt(index);
index++;
return book;
}
}
6、测试主程序
package com.dsanjun.gof.iterator;
public class Main {
public static void main(String[] args) {
BookShelf bookShelf = new BookShelf(4);
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());
}
}
}
三、Iterator 模式中的一些重要角色
Iterator模式类图
Iterator 模式中各个类的作用
四、Iterator 模式中的思路拓展
1、为什么要引入Iterator 这种复杂的设计模式?请看以下代码:
while(it.hasNext()){
Book book =(Book)it.next();
System.out.println(book.getName());
}
这里我们只是使用了Iterator接口的hasNext和next方法,并没有调用BookShelf的方法。也就是说,这里的while循环并不依赖于BookShelf的实现。如果开发人员决定放弃使用数组来管理书籍,而是用java.util.Vector替代,会怎样?不管BookShelf如何变化,只要BookShelf的iterator方法能够正确的返回Iterator 实例,即使不对上面的while循环做任何修改,代码都能正常工作。所谓“可复用”,就是指将类实现为“组件”,当一个组件发生变化时,不需要对其他的组件进行修改或是需要很小的修改即可应对。
2、使用接口编程可以弱化类之间的耦合,进而使得类更加容易作为组件被再次利用。
3、注意迭代器和持有迭代器的类是一一对应的,比如BookShelfIterator和BookShelf就是对应的关系,因为在BookShelfIterator中调用了BookShelf的方法。