迭代器模式是一种行为设计模式, 让你能在不暴露集合底层表现形式 (列表、 栈和树等) 的情况下遍历集合中所有的元素。
你计划在罗马游览数天, 参观所有主要的旅游景点。 但在到达目的地后, 你可能会浪费很多时间绕圈子, 甚至找不到罗马斗兽场在哪里。
或者你可以购买一款智能手机上的虚拟导游程序。 这款程序非常智能而且价格不贵, 你想在景点待多久都可以。
第三种选择是用部分旅行预算雇佣一位对城市了如指掌的当地向导。 向导能根据你的喜好来安排行程, 为你介绍每个景点并讲述许多激动人心的故事。 这样的旅行可能会更有趣, 但所需费用也会更高。
所有这些选择(自由漫步、 智能手机导航或真人向导)都是这个由众多罗马景点组成的集合的迭代器。
迭代器模式适合应用场景
当集合背后为复杂的数据结构, 且你希望对客户端隐藏其复杂性时 (出于使用便利性或安全性的考虑), 可以使用迭代器模式。
迭代器封装了与复杂数据结构进行交互的细节, 为客户端提供多个访问集合元素的简单方法。 这种方式不仅对客户端来说非常方便, 而且能避免客户端在直接与集合交互时执行错误或有害的操作, 从而起到保护集合的作用。
使用该模式可以减少程序中重复的遍历代码。
重要迭代算法的代码往往体积非常庞大。 当这些代码被放置在程序业务逻辑中时, 它会让原始代码的职责模糊不清, 降低其可维护性。 因此, 将遍历代码移到特定的迭代器中可使程序代码更加精炼和简洁。
如果你希望代码能够遍历不同的甚至是无法预知的数据结构, 可以使用迭代器模式。
该模式为集合和迭代器提供了一些通用接口。 如果你在代码中使用了这些接口, 那么将其他实现了这些接口的集合和迭代器传递给它时, 它仍将可以正常运行。
不多逼逼,看看TS代码
面向接口编程,使用依赖倒置原则。
使用示例: 该模式在 TypeScript 代码中很常见。 许多框架和程序库都使用它来提供遍历其集合的标准方式。
识别方法: 迭代器可以通过导航方法 (例如 next
和 previous
等) 来轻松识别。 使用迭代器的客户端代码可能没有其所遍历的集合的直接访问权限。
/**
* Iterator Design Pattern
*
* Intent: Lets you traverse elements of a collection without exposing its
* underlying representation (list, stack, tree, etc.).
*/
interface Iterator<T> {
// Return the current element.
current(): T;
// Return the current element and move forward to next element.
next(): T;
// Return the key of the current element.
key(): number;
// Checks if current position is valid.
valid(): boolean;
// Rewind the Iterator to the first element.
rewind(): void;
}
interface Aggregator {
// Retrieve an external iterator.
getIterator(): Iterator<string>;
}
/**
* Concrete Iterators implement various traversal algorithms. These classes
* store the current traversal position at all times.
*/
class AlphabeticalOrderIterator implements Iterator<string> {
private collection: WordsCollection;
/**
* Stores the current traversal position. An iterator may have a lot of
* other fields for storing iteration state, especially when it is supposed
* to work with a particular kind of collection.
*/
private position: number = 0;
/**
* This variable indicates the traversal direction.
*/
private reverse: boolean = false;
constructor(collection: WordsCollection, reverse: boolean = false) {
this.collection = collection;
this.reverse = reverse;
if (reverse) {
this.position = collection.getCount() - 1;
}
}
public rewind() {
this.position = this.reverse ?
this.collection.getCount() - 1 :
0;
}
public current(): string {
return this.collection.getItems()[this.position];
}
public key(): number {
return this.position;
}
public next(): string {
const item = this.collection.getItems()[this.position];
this.position += this.reverse ? -1 : 1;
return item;
}
public valid(): boolean {
if (this.reverse) {
return this.position >= 0;
}
return this.position < this.collection.getCount();
}
}
/**
* Concrete Collections provide one or several methods for retrieving fresh
* iterator instances, compatible with the collection class.
*/
class WordsCollection implements Aggregator {
private items: string[] = [];
public getItems(): string[] {
return this.items;
}
public getCount(): number {
return this.items.length;
}
public addItem(item: string): void {
this.items.push(item);
}
public getIterator(): Iterator<string> {
return new AlphabeticalOrderIterator(this);
}
public getReverseIterator(): Iterator<string> {
return new AlphabeticalOrderIterator(this, true);
}
}
/**
* The client code may or may not know about the Concrete Iterator or Collection
* classes, depending on the level of indirection you want to keep in your
* program.
*/
const collection = new WordsCollection();
collection.addItem('First');
collection.addItem('Second');
collection.addItem('Third');
const iterator = collection.getIterator();
console.log('Straight traversal:');
while (iterator.valid()) {
console.log(iterator.next());
}
console.log('');
console.log('Reverse traversal:');
const reverseIterator = collection.getReverseIterator();
while (reverseIterator.valid()) {
console.log(reverseIterator.next());
}
结果
Straight traversal:
First
Second
Third
Reverse traversal:
Third
Second
First