设计模式:迭代器模式

迭代器模式

提出问题

  • 有许多种方法可以将对象堆起来成为一个集合。客户想要遍历的时候,如果让客户看到集合的实现,就有些不优雅。
  • 针对不同的需要,可能需要以不同的方式遍历整个列表,即使可以预见到所需要的遍历操作,也不希望列表(指集合对象)的接口中充斥着各种不同遍历的操作。有时可能需要在同一个列表上同时进行多个遍历。
    • 例如可能需要进行过滤列表迭代器,只访问那些满足特定过滤约束条件的元素。

问题案例

应用

迭代器模式将对列表的访问和遍历从列表对象中分离出来并放入一个迭代器对象中,迭代器定义了一个访问该列表元素的接口,迭代器对象负责跟踪当前的元素,即它知道哪些元素已经遍历过了。

适用性

  • 让客户遍历对象,而又无法窥视存储对象的方式。
  • 支持对集合对象的多种遍历方式
  • 为遍历不同的集合结构提供一个统一的接口(支持多态迭代)

案例1

  • 当有一个ArrayList和一个数组要合并,但是数组对象、ArrayList有很多依赖它的对象和方法。因此不能简单地将数组转换为ArrayList。如果转换,则需要更改它本身的实现
    • 此时,对他们遍历就需要写两个循环
    • 对于不同的集合,遍历是变化的部分,则将变化的部分封装起来。即迭代器

基础概述

是什么

迭代器模式:迭代器模式提供一种方法顺序访问一集合对象中的各个元素,而又不暴露其内部的表示

1563361042968

在实例化列表迭代器前,必须提供待遍历的列表,一旦有了该列表迭代器的实例就可以顺序地访问该列表的各个元素。

将遍历机制与列表对象分离可以让我们定义不同的迭代器来实现不同的遍历策略,而不需要在列表接口中列举它们。

迭代器是与集合耦合在一起的,而且客户对象必须知道遍历的是一个列表而不是其他集合结构。则最好能够有一种办法使得不需要改变客户代码即可改变该集合类,则通过将迭代器的概念推广到多态迭代器来达到目标。

因此我们需要定义一个抽象类AbstrcatList,提供操作列表的公共接口,也需要一个抽象的迭代器类Iterator,然后为每个不同的列表实现定义具体的Iterator子类,此时迭代机制就与具体的集合类无关了。

1563363965380

迭代器的类方案确定后,就是如何创建迭代器,既然使代码不依赖于具体的列表子类,则不能仅仅简单地实例化一个特定的类,而需要让列表对象负责创建相应的迭代器,需要列表对象提供CreateIterator的操作。

创建迭代器是一个Factory Method的例子,用它来使得一个客户可以向一个列表对象请求合适的迭代器。

分类

协作

结构

1555743128206

1555743292853

1563364193922

参与者

  • Iterator:迭代器,定义访问和遍历元素的接口
  • ConcreteIterator:具体迭代器。
    • 实现迭代器接口,集合遍历时跟踪当前位置
  • Aggregate:集合,定义创建相应迭代器对象的接口
  • ConcreteIterator:具体集合,实现相应迭代器接口,该操作返回一个ConcreteIterator的适当实例

协作

  • 类关系
    • 参与者
  • 逻辑关系
    • ConcreIterator跟踪集合中的当前对象,并能够计算出待遍历的后继对象

权衡

  • 谁控制该迭代
  • 谁定义遍历算法
  • 迭代器的健壮程度

分类

结构

效果(优缺)

  • 支持以不同的方式遍历一个集合
    • 复杂的集合可用多种方式进行遍历,如代码生成和语义检查要遍历语法分析树,代码生成可以按中序或者前序来遍历语法分析树。迭代器模式使得改变遍历算法变得很容易,仅需要用一个不同的迭代器实例即可。
  • 简化了集合的接口
    • 有了迭代器的遍历接口,集合本身就不再需要类似的遍历接口了,即简化了集合的接口
  • 在同一个集合上可以有多个遍历
    • 每个迭代器保持它自己的遍历状态,因此可以同时进行多个遍历
  • 附加的迭代器操作
    • 可以为迭代器做一些增强性的功能,例如previous操作等
  • 用于复合对象的迭代器
    • 在Composite模式中的那些递归聚合结构上,外部迭代器可能难以实现,因为在该结构中不同对象处于嵌套集合的多个不同层次。因此一个外部迭代器为跟踪当前对象必须存储一条纵贯该Composite的路径
    • 有时使用一个内部迭代器更容易一些,仅需要递归调用自己即可,隐式将路径存储在调用栈当中,无需显式维护当前对象位置
  • 空迭代器
    • 是退化的迭代器,有助于处理边界条件。根据定义一个空迭代器总是已经完成了遍历,即永远是True

实现

实现步骤

依赖于一个名为迭代器的接口

案例1

迭代器接口

1
2
3
4
5
6
public interface Iterator{
//判断是否还有更多的元素
boolean hasNext();
//返回下一个元素
Object next();
}

数组实现迭代器

  • 实现接口方法
  • 实现迭代器
  • 让对象实现返回迭代器的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//迭代器的实现
public class MenuIterator implements Iterator {
MenuItem[] items;
//标记目前遍历到达的位置
int position;

public MenuIterator(MenuItem[] menuItem) {
this.items = menuItem;
}

@Override
public boolean hasNext() {
if (position >= items.length || items[position] == null)
return false;
else
return true;
}

@Override
public Object next() {
MenuItem menuItem = items[position];
position++;
return menuItem;
}
}
1
2
3
4
5
6
7
8
9
10
11
//对象返回迭代器
public class DinerMenu {
static final int MAX_ITEMS = 6;
int numberOfItems = 0;
MenuItem[] menuItems;

//返回迭代器接口,客户只需要使用迭代器遍历即可
public Iterator createIterator() {
return new MenuIterator(menuItems);
}
}

相关模式

  • Composite:迭代器常被应用到像符合这样的递归结构上
  • Factory Method:多态迭代器靠Factory Method来实例适当的迭代器子类
  • Memento:常与迭代器模式一起使用,迭代器使用一个memento来捕获一个迭代的状态,迭代器在其内部存储memento

进阶

反省总结

###

参考