设计模式学习笔记(十八)迭代器模式
概念
迭代器模式是一种行为设计模式,它提供了一种顺序访问聚合对象中各个元素的方法,而无需暴露聚合对象的内部结构。迭代器模式将对集合对象的操作与遍历方式分离,使得可以独立地改变遍历算法。
一个适合迭代器模式的例子是一个图书馆管理系统,其中有一个书架(集合对象),存放着多本图书(元素对象)。我们希望能够遍历这个书架中的图书。
如果不使用迭代器模式,我们可能需要在客户端代码中既编写数据的维护又编写循环遍历书架中的图书。这样的代码可能会比较冗长,而且违反了“单一职责原则”,并且需要直接暴露书架的内部数据结构给客户端代码。
我们可以将数据维护和数据的遍历分离出来,为此可以定义一个图书迭代器对象,也就是迭代器类,它实现了统一的遍历接口。这个迭代器对象知道如何遍历书架并获取图书。客户端可以通过迭代器对象来遍历书架,而不需要了解书架类的内部结构。
● Iterator(抽象迭代器):它定义了访问和遍历元素的接口,声明了用于遍历数据元素的方法,例如:用于获取第一个元素的first()方法,用于访问下一个元素的next()方法,用于判断是否还有下一个元素的hasNext()方法,用于获取当前元素的currentItem()方法等,在具体迭代器中将实现这些方法。
● ConcreteIterator(具体迭代器):它实现了抽象迭代器接口,完成对聚合对象的遍历,同时在具体迭代器中通过游标来记录在聚合对象中所处的当前位置,在具体实现时,游标通常是一个表示位置的非负整数。
● Aggregate(抽象聚合类):它用于存储和管理元素对象,声明一个createIterator()方法用于创建一个迭代器对象,充当抽象迭代器工厂角色。
● ConcreteAggregate(具体聚合类):它实现了在抽象聚合类中声明的createIterator()方法,该方法返回一个与该具体聚合类对应的具体迭代器ConcreteIterator实例。
此图中,我们声明了两个部分,聚合类与迭代器类,结合工厂方法模式将迭代器类替换原来的工厂类。 在迭代器模式中,提供了一个外部的迭代器来对聚合对象进行访问和遍历,迭代器定义了一个访问该聚合元素的接口,并且可以跟踪当前遍历的元素,了解哪些元素已经遍历过而哪些没有。迭代器的引入,将使得对一个复杂聚合对象的操作变得简单。
示例
图书管理系统
使用聚合类与迭代器类关联关系实现
我们将要使用迭代器模式实现图书的维护及遍历
//抽象聚合类
abstract class AbstractObjectList {
protected List<Object> objects = new ArrayList<Object>();
public AbstractObjectList(List objects) {
this.objects = objects;
}
public void addObject(Object obj) {
this.objects.add(obj);
}
public void removeObject(Object obj) {
this.objects.remove(obj);
}
public List getObjects() {
return this.objects;
}
//声明创建迭代器对象的抽象工厂方法
public abstract AbstractIterator createIterator();
}
//图书类:具体聚合类
class BookList extends AbstractObjectList {
public BookList(List books) {
super(books);
}
//实现创建迭代器对象的具体工厂方法
@Override
public AbstractIterator createIterator() {
return new BookIterator(this);
}
}
//抽象迭代器
interface AbstractIterator {
public boolean hasNext(); //判断是否有下一个元素
public boolean hasPre(); //判断是否有上一个元素
public Object getNext(); //得到下一个元素
public Object getPre(); //得到上一个元素
public boolean isLast(); //判断是否为最后一个元素
public boolean isFirst(); //判断是否为第一个元素
public Object getCurrentItem(); //获取当前元素
}
//图书迭代器:具体迭代器
class BookIterator implements AbstractIterator {
private List books;
private int cursor; //定义一个游标
public BookIterator(BookList list) {
this.books = list.getObjects(); //获取集合对象
cursor = 0; //设置正向遍历游标的初始值
}
@Override
public boolean hasNext() {
return cursor <= books.size();
}
@Override
public boolean hasPre() {
return cursor >= 0;
}
@Override
public Object getNext() {
if (hasNext()) {
Object o = books.get(cursor);
cursor++;
return o;
}else {
return null;
}
}
@Override
public Object getPre() {
if (hasPre()) {
Object o = books.get(cursor);
cursor--;
return o;
}else {
return null;
}
}
@Override
public boolean isLast() {
if (cursor == books.size()) {
cursor = books.size() - 1;
return true;
}else {
return false;
}
}
@Override
public boolean isFirst() {
if (cursor == -1) {
cursor = 0;
return true;
}else {
return false;
}
}
@Override
public Object getCurrentItem() {
return books.get(cursor);
}
}
客户端
List<String> books = new ArrayList();
books.add("三国演义");
books.add("水浒传");
books.add("红楼梦");
books.add("西游记");
//创建图书聚合类
AbstractObjectList list = new BookList(books);
//创建图书类迭代器
AbstractIterator iterator = list.createIterator();
System.out.println("正向遍历----------------");
while (!iterator.isLast()) {
System.out.println(iterator.getNext());
}
System.out.println("逆向遍历----------------");
while (!iterator.isFirst()) {
System.out.println(iterator.getPre());
}
输出结果
正向遍历----------------
三国演义
水浒传
红楼梦
西游记
逆向遍历----------------
西游记
红楼梦
水浒传
三国演义
注:工厂方法模式的实现是工厂类和产品类的结合,在客户端中由工厂创建产品;而迭代器结合的工厂方法模式下的客户端中由聚合类(对应产品)创建迭代器。
将迭代器类作为聚合类的内部类方式实现
将具体迭代器类放入具体聚合类中作为内部类,其他类与关联关系实现方式一样
//图书类:具体聚合类
class BookList extends AbstractObjectList {
public BookList(List books) {
super(books);
}
//实现创建迭代器对象的具体工厂方法
@Override
public AbstractIterator createIterator() {
return new BookIterator(this);
}
class BookIterator implements AbstractIterator {
private List books;
private int cursor; //定义一个游标
public BookIterator(BookList list) {
this.books = list.getObjects(); //获取集合对象
cursor = 0; //设置正向遍历游标的初始值
}
@Override
public boolean hasNext() {
return cursor <= books.size();
}
@Override
public boolean hasPre() {
return cursor >= 0;
}
@Override
public Object getNext() {
if (hasNext()) {
Object o = books.get(cursor);
cursor++;
return o;
}else {
return null;
}
}
@Override
public Object getPre() {
if (hasPre()) {
Object o = books.get(cursor);
cursor--;
return o;
}else {
return null;
}
}
@Override
public boolean isLast() {
if (cursor == books.size()) {
cursor = books.size() - 1;
return true;
}else {
return false;
}
}
@Override
public boolean isFirst() {
if (cursor == -1) {
cursor = 0;
return true;
}else {
return false;
}
}
@Override
public Object getCurrentItem() {
return books.get(cursor);
}
}
}
JDK内置的迭代器
总结
优点:
1、简化了聚合对象的接口:迭代器模式将遍历元素的责任封装在迭代器中,聚合对象只需要提供一个迭代器对象即可,简化了聚合对象的接口。
2、支持多种遍历方式:迭代器模式可以根据需求实现不同的迭代方式,例如顺序遍历、逆序遍历等,而不需要修改聚合对象的代码。
3、封装了遍历算法:迭代器模式将遍历算法封装在迭代器中,客户端无需关心具体的遍历实现细节,方便了代码的重用和维护。
4、增强了代码的灵活性和可扩展性:由于聚合对象与迭代器之间解耦,可以方便地替换或新增新的迭代器,而无需修改聚合对象的代码。
缺点:
1、增加了类的个数:引入迭代器会增加额外的迭代器类,增加了代码量和类的数量。
2、迭代器遍历时的效率问题:迭代器模式适用于访问稳定、数据量不大的聚合对象,如果聚合对象的数据量庞大,使用迭代器可能会有一定的性能影响。
适用场景:
1、需要遍历一个聚合对象,而又不希望暴露其内部结构的情况。
2、需要提供多种遍历方式的聚合对象,而又不希望聚合对象和遍历算法之间有耦合关系的情况。
3、希望简化处理聚合对象的代码,将遍历元素的责任分离出去的情况。
4、需要在不同的上下文中遍历一个聚合对象,而又不希望每次都重新实现遍历逻辑的情况。
总之,迭代器模式在需要遍历聚合对象并统一访问方式的场景下十分有用,它可以提供灵活且高度封装的遍历接口,同时能够增加代码的可维护性和可扩展性。