https://refactoringguru.cn/design-patterns/iterator

行为模式-迭代器模式

亦称: Iterator

意图

迭代器模式是一种行为设计模式, 让你能在不暴露集合底层表现形式 (列表、 栈和树等) 的情况下遍历集合中所有的元素。

问题

集合是编程中最常使用的数据类型之一。 尽管如此, 集合只是一组对象的容器而已。

各种类型的集合。

大部分集合使用简单列表存储元素。 但有些集合还会使用栈、 树、 图和其他复杂的数据结构。

无论集合的构成方式如何, 它都必须提供某种访问元素的方式, 便于其他代码使用其中的元素。 集合应提供一种能够遍历元素的方式, 且保证它不会周而复始地访问同一个元素。

如果你的集合基于列表, 那么这项工作听上去仿佛很简单。 但如何遍历复杂数据结构 (例如树) 中的元素呢? 例如, 今天你需要使用深度优先算法来遍历树结构, 明天可能会需要广度优先算法; 下周则可能会需要其他方式 (比如随机存取树中的元素)。

可通过不同的方式遍历相同的集合。

不断向集合中添加遍历算法会模糊其 “高效存储数据” 的主要职责。 此外, 有些算法可能是根据特定应用订制的, 将其加入泛型集合类中会显得非常奇怪。

另一方面, 使用多种集合的客户端代码可能并不关心存储数据的方式。 不过由于集合提供不同的元素访问方式, 你的代码将不得不与特定集合类进行耦合。

解决方案

迭代器模式的主要思想是将集合的遍历行为抽取为单独的迭代器对象。

迭代器可实现多种遍历算法。 多个迭代器对象可同时遍历同一个集合。

除实现自身算法外, 迭代器还封装了遍历操作的所有细节, 例如当前位置和末尾剩余元素的数量。 因此, 多个迭代器可以在相互独立的情况下同时访问集合。

迭代器通常会提供一个获取集合元素的基本方法。 客户端可不断调用该方法直至它不返回任何内容, 这意味着迭代器已经遍历了所有元素。

所有迭代器必须实现相同的接口。 这样一来, 只要有合适的迭代器, 客户端代码就能兼容任何类型的集合或遍历算法。 如果你需要采用特殊方式来遍历集合, 只需创建一个新的迭代器类即可, 无需对集合或客户端进行修改。

真实世界类比

漫步罗马的不同方式。

你计划在罗马游览数天, 参观所有主要的旅游景点。 但在到达目的地后, 你可能会浪费很多时间绕圈子, 甚至找不到罗马斗兽场在哪里。

或者你可以购买一款智能手机上的虚拟导游程序。 这款程序非常智能而且价格不贵, 你想在景点待多久都可以。

第三种选择是用部分旅行预算雇佣一位对城市了如指掌的当地向导。 向导能根据你的喜好来安排行程, 为你介绍每个景点并讲述许多激动人心的故事。 这样的旅行可能会更有趣, 但所需费用也会更高。

所有这些选择(自由漫步、 智能手机导航或真人向导)都是这个由众多罗马景点组成的集合的迭代器。

迭代器模式结构

  1. 迭代器 (Iterator) 接口声明了遍历集合所需的操作: 获取下一个元素、 获取当前位置和重新开始迭代等。

  2. 具体迭代器 (Concrete Iterators) 实现遍历集合的一种特定算法。 迭代器对象必须跟踪自身遍历的进度。 这使得多个迭代器可以相互独立地遍历同一集合。

  3. 集合 (Collection) 接口声明一个或多个方法来获取与集合兼容的迭代器。 请注意, 返回方法的类型必须被声明为迭代器接口, 因此具体集合可以返回各种不同种类的迭代器。

  4. 具体集合 (Concrete Collections) 会在客户端请求迭代器时返回一个特定的具体迭代器类实体。 你可能会琢磨, 剩下的集合代码在什么地方呢? 不用担心, 它也会在同一个类中。 只是这些细节对于实际模式来说并不重要, 所以我们将其省略了而已。

  5. 客户端 (Client) 通过集合和迭代器的接口与两者进行交互。 这样一来客户端无需与具体类进行耦合, 允许同一客户端代码使用各种不同的集合和迭代器。

    客户端通常不会自行创建迭代器, 而是会从集合中获取。 但在特定情况下, 客户端可以直接创建一个迭代器 (例如当客户端需要自定义特殊迭代器时)。

伪代码

在本例中, 迭代器模式用于遍历一个封装了访问微信好友关系功能的特殊集合。 该集合提供使用不同方式遍历档案资料的多个迭代器。

遍历社交档案的示例

“好友 (friends)” 迭代器可用于遍历指定档案的好友。 “同事 (colleagues)” 迭代器也提供同样的功能, 但仅包括与目标用户在同一家公司工作的好友。 这两个迭代器都实现了同一个通用接口, 客户端能在不了解认证和发送 REST 请求等实现细节的情况下获取档案。

客户端仅通过接口与集合和迭代器交互, 也就不会同具体类耦合。 如果你决定将应用连接到全新的社交网络, 只需提供新的集合和迭代器类即可, 无需修改现有代码。

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
// 集合接口必须声明一个用于生成迭代器的工厂方法。如果程序中有不同类型的迭
// 代器,你也可以声明多个方法。
interface SocialNetwork is
method createFriendsIterator(profileId):ProfileIterator
method createCoworkersIterator(profileId):ProfileIterator


// 每个具体集合都与其返回的一组具体迭代器相耦合。但客户并不是这样的,因为
// 这些方法的签名将会返回迭代器接口。
class WeChat implements SocialNetwork is
// ...大量的集合代码应该放在这里...

// 迭代器创建代码。
method createFriendsIterator(profileId) is
return new WeChatIterator(this, profileId, "friends")
method createCoworkersIterator(profileId) is
return new WeChatIterator(this, profileId, "coworkers")


// 所有迭代器的通用接口。
interface ProfileIterator is
method getNext():Profile
method hasMore():bool


// 具体迭代器类。
class WeChatIterator implements ProfileIterator is
// 迭代器需要一个指向其遍历集合的引用。
private field weChat: WeChat
private field profileId, type: string

// 迭代器对象会独立于其他迭代器来对集合进行遍历。因此它必须保存迭代器
// 的状态。
private field currentPosition
private field cache: array of Profile

constructor WeChatIterator(weChat, profileId, type) is
this.weChat = weChat
this.profileId = profileId
this.type = type

private method lazyInit() is
if (cache == null)
cache = weChat.socialGraphRequest(profileId, type)

// 每个具体迭代器类都会自行实现通用迭代器接口。
method getNext() is
if (hasMore())
currentPosition++
return cache[currentPosition]

method hasMore() is
lazyInit()
return currentPosition < cache.length


// 这里还有一个有用的绝招:你可将迭代器传递给客户端类,无需让其拥有访问整
// 个集合的权限。这样一来,你就无需将集合暴露给客户端了。
//
// 还有另一个好处:你可在运行时将不同的迭代器传递给客户端,从而改变客户端
// 与集合互动的方式。这一方法可行的原因是客户端代码并没有和具体迭代器类相
// 耦合。
class SocialSpammer is
method send(iterator: ProfileIterator, message: string) is
while (iterator.hasMore())
profile = iterator.getNext()
System.sendEmail(profile.getEmail(), message)


// 应用程序(Application)类可对集合和迭代器进行配置,然后将其传递给客户
// 端代码。
class Application is
field network: SocialNetwork
field spammer: SocialSpammer

method config() is
if working with WeChat
this.network = new WeChat()
if working with LinkedIn
this.network = new LinkedIn()
this.spammer = new SocialSpammer()

method sendSpamToFriends(profile) is
iterator = network.createFriendsIterator(profile.getId())
spammer.send(iterator, "非常重要的消息")

method sendSpamToCoworkers(profile) is
iterator = network.createCoworkersIterator(profile.getId())
spammer.send(iterator, "非常重要的消息")

迭代器模式适合应用场景

当集合背后为复杂的数据结构,且你希望对客户端隐藏其复杂性时(出于使用便利性或安全性的考虑),可以使用迭代器模式。

迭代器封装了与复杂数据结构进行交互的细节, 为客户端提供多个访问集合元素的简单方法。 这种方式不仅对客户端来说非常方便, 而且能避免客户端在直接与集合交互时执行错误或有害的操作, 从而起到保护集合的作用。

使用该模式可以减少程序中重复的遍历代码。

重要迭代算法的代码往往体积非常庞大。 当这些代码被放置在程序业务逻辑中时, 它会让原始代码的职责模糊不清, 降低其可维护性。 因此, 将遍历代码移到特定的迭代器中可使程序代码更加精炼和简洁。

如果你希望代码能够遍历不同的甚至是无法预知的数据结构,可以使用迭代器模式。

该模式为集合和迭代器提供了一些通用接口。 如果你在代码中使用了这些接口, 那么将其他实现了这些接口的集合和迭代器传递给它时, 它仍将可以正常运行。

实现方式

  1. 声明迭代器接口。 该接口必须提供至少一个方法来获取集合中的下个元素。 但为了使用方便, 你还可以添加一些其他方法, 例如获取前一个元素、 记录当前位置和判断迭代是否已结束。
  2. 声明集合接口并描述一个获取迭代器的方法。 其返回值必须是迭代器接口。 如果你计划拥有多组不同的迭代器, 则可以声明多个类似的方法。
  3. 为希望使用迭代器进行遍历的集合实现具体迭代器类。 迭代器对象必须与单个集合实体链接。 链接关系通常通过迭代器的构造函数建立。
  4. 在你的集合类中实现集合接口。 其主要思想是针对特定集合为客户端代码提供创建迭代器的快捷方式。 集合对象必须将自身传递给迭代器的构造函数来创建两者之间的链接。
  5. 检查客户端代码, 使用迭代器替代所有集合遍历代码。 每当客户端需要遍历集合元素时都会获取一个新的迭代器。

迭代器模式优缺点

优点

  • 单一职责原则。 通过将体积庞大的遍历算法代码抽取为独立的类, 你可对客户端代码和集合进行整理。
  • 开闭原则。 你可实现新型的集合和迭代器并将其传递给现有代码, 无需修改现有代码。
  • 你可以并行遍历同一集合, 因为每个迭代器对象都包含其自身的遍历状态。
  • 相似的, 你可以暂停遍历并在需要时继续。

缺点

  • 如果你的程序只与简单的集合进行交互, 应用该模式可能会矫枉过正。
  • 对于某些特殊集合, 使用迭代器可能比直接遍历的效率低。

与其他模式的关系

代码示例

迭代器是一种行为设计模式, 让你能在不暴露复杂数据结构内部细节的情况下遍历其中所有的元素。

在迭代器的帮助下, 客户端可以用一个迭代器接口以相似的方式遍历不同集合中的元素。

在 C# 中使用模式

复杂度: ★★☆

流行度: ★★★

使用示例: 该模式在 C# 代码中很常见。 许多框架和程序库都使用它来提供遍历其集合的标准方式。

识别方法: 迭代器可以通过导航方法 (例如 nextprevious等) 来轻松识别。 使用迭代器的客户端代码可能没有其所遍历的集合的直接访问权限。

概念示例

本例说明了迭代器设计模式的结构并重点回答了下面的问题:

  • 它由哪些类组成?
  • 这些类扮演了哪些角色?
  • 模式中的各个元素会以何种方式相互关联?

Program.cs: 概念示例

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
using System;
using System.Collections;
using System.Collections.Generic;

namespace RefactoringGuru.DesignPatterns.Iterator.Conceptual
{
abstract class Iterator : IEnumerator
{
object IEnumerator.Current => Current();

// Returns the key of the current element
public abstract int Key();

// Returns the current element
public abstract object Current();

// Move forward to next element
public abstract bool MoveNext();

// Rewinds the Iterator to the first element
public abstract void Reset();
}

abstract class IteratorAggregate : IEnumerable
{
// Returns an Iterator or another IteratorAggregate for the implementing
// object.
public abstract IEnumerator GetEnumerator();
}

// Concrete Iterators implement various traversal algorithms. These classes
// store the current traversal position at all times.
class AlphabeticalOrderIterator : Iterator
{
private WordsCollection _collection;

// 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 int _position = -1;

private bool _reverse = false;

public AlphabeticalOrderIterator(WordsCollection collection, bool reverse = false)
{
this._collection = collection;
this._reverse = reverse;

if (reverse)
{
this._position = collection.getItems().Count;
}
}

public override object Current()
{
return this._collection.getItems()[_position];
}

public override int Key()
{
return this._position;
}

public override bool MoveNext()
{
int updatedPosition = this._position + (this._reverse ? -1 : 1);

if (updatedPosition >= 0 && updatedPosition < this._collection.getItems().Count)
{
this._position = updatedPosition;
return true;
}
else
{
return false;
}
}

public override void Reset()
{
this._position = this._reverse ? this._collection.getItems().Count - 1 : 0;
}
}

// Concrete Collections provide one or several methods for retrieving fresh
// iterator instances, compatible with the collection class.
class WordsCollection : IteratorAggregate
{
List<string> _collection = new List<string>();

bool _direction = false;

public void ReverseDirection()
{
_direction = !_direction;
}

public List<string> getItems()
{
return _collection;
}

public void AddItem(string item)
{
this._collection.Add(item);
}

public override IEnumerator GetEnumerator()
{
return new AlphabeticalOrderIterator(this, _direction);
}
}

class Program
{
static void Main(string[] args)
{
// 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.
var collection = new WordsCollection();
collection.AddItem("First");
collection.AddItem("Second");
collection.AddItem("Third");

Console.WriteLine("Straight traversal:");

foreach (var element in collection)
{
Console.WriteLine(element);
}

Console.WriteLine("\nReverse traversal:");

collection.ReverseDirection();

foreach (var element in collection)
{
Console.WriteLine(element);
}
}
}
}

Output.txt: 执行结果

1
2
3
4
5
6
7
8
9
Straight traversal:
First
Second
Third

Reverse traversal:
Third
Second
First

在 C++ 中使用模式

复杂度: ★★☆

流行度: ★★★

使用示例: 该模式在 C++ 代码中很常见。 许多框架和程序库都使用它来提供遍历其集合的标准方式。

识别方法: 迭代器可以通过导航方法 (例如 nextprevious等) 来轻松识别。 使用迭代器的客户端代码可能没有其所遍历的集合的直接访问权限。

概念示例

本例说明了迭代器设计模式的结构并重点回答了下面的问题:

  • 它由哪些类组成?
  • 这些类扮演了哪些角色?
  • 模式中的各个元素会以何种方式相互关联?

main.cc: 概念示例

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
/**
* Iterator Design Pattern
*
* Intent: Lets you traverse elements of a collection without exposing its
* underlying representation (list, stack, tree, etc.).
*/

#include <iostream>
#include <string>
#include <vector>

/**
* C++ has its own implementation of iterator that works with a different
* generics containers defined by the standard library.
*/

template <typename T, typename U>
class Iterator {
public:
typedef typename std::vector<T>::iterator iter_type;
Iterator(U *p_data, bool reverse = false) : m_p_data_(p_data) {
m_it_ = m_p_data_->m_data_.begin();
}

void First() {
m_it_ = m_p_data_->m_data_.begin();
}

void Next() {
m_it_++;
}

bool IsDone() {
return (m_it_ == m_p_data_->m_data_.end());
}

iter_type Current() {
return m_it_;
}

private:
U *m_p_data_;
iter_type m_it_;
};

/**
* Generic Collections/Containers provides one or several methods for retrieving
* fresh iterator instances, compatible with the collection class.
*/

template <class T>
class Container {
friend class Iterator<T, Container>;

public:
void Add(T a) {
m_data_.push_back(a);
}

Iterator<T, Container> *CreateIterator() {
return new Iterator<T, Container>(this);
}

private:
std::vector<T> m_data_;
};

class Data {
public:
Data(int a = 0) : m_data_(a) {}

void set_data(int a) {
m_data_ = a;
}

int data() {
return m_data_;
}

private:
int m_data_;
};

/**
* The client code may or may not know about the Concrete Iterator or Collection
* classes, for this implementation the container is generic so you can used
* with an int or with a custom class.
*/
void ClientCode() {
std::cout << "________________Iterator with int______________________________________" << std::endl;
Container<int> cont;

for (int i = 0; i < 10; i++) {
cont.Add(i);
}

Iterator<int, Container<int>> *it = cont.CreateIterator();
for (it->First(); !it->IsDone(); it->Next()) {
std::cout << *it->Current() << std::endl;
}

Container<Data> cont2;
Data a(100), b(1000), c(10000);
cont2.Add(a);
cont2.Add(b);
cont2.Add(c);

std::cout << "________________Iterator with custom Class______________________________" << std::endl;
Iterator<Data, Container<Data>> *it2 = cont2.CreateIterator();
for (it2->First(); !it2->IsDone(); it2->Next()) {
std::cout << it2->Current()->data() << std::endl;
}
delete it;
delete it2;
}

int main() {
ClientCode();
return 0;
}

Output.txt: 执行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
________________Iterator with int______________________________________
0
1
2
3
4
5
6
7
8
9
________________Iterator with custom Class______________________________
100
1000
10000

在 Java 中使用模式

复杂度: ★★☆

流行度: ★★★

使用示例: 该模式在 Java 代码中很常见。 许多框架和程序库都会使用它来提供遍历其集合的标准方式。

下面是该模式在核心 Java 程序库中的一些示例:

识别方法: 迭代器可以通过导航方法 (例如 nextprevious等) 来轻松识别。 使用迭代器的客户端代码可能没有其所遍历的集合的直接访问权限。

迭代访问社交网络档案

在本例中, 迭代器模式被用于在不向客户端代码暴露通信细节的情况下访问远程社交网络集合中的社交媒体档案。

iterators

iterators/ProfileIterator.java: 定义档案接口

1
2
3
4
5
6
7
8
9
10
11
package refactoring_guru.iterator.example.iterators;

import refactoring_guru.iterator.example.profile.Profile;

public interface ProfileIterator {
boolean hasNext();

Profile getNext();

void reset();
}

iterators/FacebookIterator.java: 在 Facebook 档案上实现迭代

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
package refactoring_guru.iterator.example.iterators;

import refactoring_guru.iterator.example.profile.Profile;
import refactoring_guru.iterator.example.social_networks.Facebook;

import java.util.ArrayList;
import java.util.List;

public class FacebookIterator implements ProfileIterator {
private Facebook facebook;
private String type;
private String email;
private int currentPosition = 0;
private List<String> emails = new ArrayList<>();
private List<Profile> profiles = new ArrayList<>();

public FacebookIterator(Facebook facebook, String type, String email) {
this.facebook = facebook;
this.type = type;
this.email = email;
}

private void lazyLoad() {
if (emails.size() == 0) {
List<String> profiles = facebook.requestProfileFriendsFromFacebook(this.email, this.type);
for (String profile : profiles) {
this.emails.add(profile);
this.profiles.add(null);
}
}
}

@Override
public boolean hasNext() {
lazyLoad();
return currentPosition < emails.size();
}

@Override
public Profile getNext() {
if (!hasNext()) {
return null;
}

String friendEmail = emails.get(currentPosition);
Profile friendProfile = profiles.get(currentPosition);
if (friendProfile == null) {
friendProfile = facebook.requestProfileFromFacebook(friendEmail);
profiles.set(currentPosition, friendProfile);
}
currentPosition++;
return friendProfile;
}

@Override
public void reset() {
currentPosition = 0;
}
}

iterators/LinkedInIterator.java: 在领英档案上实现迭代

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
package refactoring_guru.iterator.example.iterators;

import refactoring_guru.iterator.example.profile.Profile;
import refactoring_guru.iterator.example.social_networks.LinkedIn;

import java.util.ArrayList;
import java.util.List;

public class LinkedInIterator implements ProfileIterator {
private LinkedIn linkedIn;
private String type;
private String email;
private int currentPosition = 0;
private List<String> emails = new ArrayList<>();
private List<Profile> contacts = new ArrayList<>();

public LinkedInIterator(LinkedIn linkedIn, String type, String email) {
this.linkedIn = linkedIn;
this.type = type;
this.email = email;
}

private void lazyLoad() {
if (emails.size() == 0) {
List<String> profiles = linkedIn.requestRelatedContactsFromLinkedInAPI(this.email, this.type);
for (String profile : profiles) {
this.emails.add(profile);
this.contacts.add(null);
}
}
}

@Override
public boolean hasNext() {
lazyLoad();
return currentPosition < emails.size();
}

@Override
public Profile getNext() {
if (!hasNext()) {
return null;
}

String friendEmail = emails.get(currentPosition);
Profile friendContact = contacts.get(currentPosition);
if (friendContact == null) {
friendContact = linkedIn.requestContactInfoFromLinkedInAPI(friendEmail);
contacts.set(currentPosition, friendContact);
}
currentPosition++;
return friendContact;
}

@Override
public void reset() {
currentPosition = 0;
}
}

social_networks

social_networks/SocialNetwork.java: 定义通用的社交网络接口

1
2
3
4
5
6
7
8
9
package refactoring_guru.iterator.example.social_networks;

import refactoring_guru.iterator.example.iterators.ProfileIterator;

public interface SocialNetwork {
ProfileIterator createFriendsIterator(String profileEmail);

ProfileIterator createCoworkersIterator(String profileEmail);
}

social_networks/Facebook.java: Facebook

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
package refactoring_guru.iterator.example.social_networks;

import refactoring_guru.iterator.example.iterators.FacebookIterator;
import refactoring_guru.iterator.example.iterators.ProfileIterator;
import refactoring_guru.iterator.example.profile.Profile;

import java.util.ArrayList;
import java.util.List;

public class Facebook implements SocialNetwork {
private List<Profile> profiles;

public Facebook(List<Profile> cache) {
if (cache != null) {
this.profiles = cache;
} else {
this.profiles = new ArrayList<>();
}
}

public Profile requestProfileFromFacebook(String profileEmail) {
// Here would be a POST request to one of the Facebook API endpoints.
// Instead, we emulates long network connection, which you would expect
// in the real life...
simulateNetworkLatency();
System.out.println("Facebook: Loading profile '" + profileEmail + "' over the network...");

// ...and return test data.
return findProfile(profileEmail);
}

public List<String> requestProfileFriendsFromFacebook(String profileEmail, String contactType) {
// Here would be a POST request to one of the Facebook API endpoints.
// Instead, we emulates long network connection, which you would expect
// in the real life...
simulateNetworkLatency();
System.out.println("Facebook: Loading '" + contactType + "' list of '" + profileEmail + "' over the network...");

// ...and return test data.
Profile profile = findProfile(profileEmail);
if (profile != null) {
return profile.getContacts(contactType);
}
return null;
}

private Profile findProfile(String profileEmail) {
for (Profile profile : profiles) {
if (profile.getEmail().equals(profileEmail)) {
return profile;
}
}
return null;
}

private void simulateNetworkLatency() {
try {
Thread.sleep(2500);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}

@Override
public ProfileIterator createFriendsIterator(String profileEmail) {
return new FacebookIterator(this, "friends", profileEmail);
}

@Override
public ProfileIterator createCoworkersIterator(String profileEmail) {
return new FacebookIterator(this, "coworkers", profileEmail);
}

}

social_networks/LinkedIn.java: 领英

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
package refactoring_guru.iterator.example.social_networks;

import refactoring_guru.iterator.example.iterators.LinkedInIterator;
import refactoring_guru.iterator.example.iterators.ProfileIterator;
import refactoring_guru.iterator.example.profile.Profile;

import java.util.ArrayList;
import java.util.List;

public class LinkedIn implements SocialNetwork {
private List<Profile> contacts;

public LinkedIn(List<Profile> cache) {
if (cache != null) {
this.contacts = cache;
} else {
this.contacts = new ArrayList<>();
}
}

public Profile requestContactInfoFromLinkedInAPI(String profileEmail) {
// Here would be a POST request to one of the LinkedIn API endpoints.
// Instead, we emulates long network connection, which you would expect
// in the real life...
simulateNetworkLatency();
System.out.println("LinkedIn: Loading profile '" + profileEmail + "' over the network...");

// ...and return test data.
return findContact(profileEmail);
}

public List<String> requestRelatedContactsFromLinkedInAPI(String profileEmail, String contactType) {
// Here would be a POST request to one of the LinkedIn API endpoints.
// Instead, we emulates long network connection, which you would expect
// in the real life.
simulateNetworkLatency();
System.out.println("LinkedIn: Loading '" + contactType + "' list of '" + profileEmail + "' over the network...");

// ...and return test data.
Profile profile = findContact(profileEmail);
if (profile != null) {
return profile.getContacts(contactType);
}
return null;
}

private Profile findContact(String profileEmail) {
for (Profile profile : contacts) {
if (profile.getEmail().equals(profileEmail)) {
return profile;
}
}
return null;
}

private void simulateNetworkLatency() {
try {
Thread.sleep(2500);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}

@Override
public ProfileIterator createFriendsIterator(String profileEmail) {
return new LinkedInIterator(this, "friends", profileEmail);
}

@Override
public ProfileIterator createCoworkersIterator(String profileEmail) {
return new LinkedInIterator(this, "coworkers", profileEmail);
}
}

profile

profile/Profile.java: 社交档案

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package refactoring_guru.iterator.example.profile;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Profile {
private String name;
private String email;
private Map<String, List<String>> contacts = new HashMap<>();

public Profile(String email, String name, String... contacts) {
this.email = email;
this.name = name;

// Parse contact list from a set of "friend:email@gmail.com" pairs.
for (String contact : contacts) {
String[] parts = contact.split(":");
String contactType = "friend", contactEmail;
if (parts.length == 1) {
contactEmail = parts[0];
}
else {
contactType = parts[0];
contactEmail = parts[1];
}
if (!this.contacts.containsKey(contactType)) {
this.contacts.put(contactType, new ArrayList<>());
}
this.contacts.get(contactType).add(contactEmail);
}
}

public String getEmail() {
return email;
}

public String getName() {
return name;
}

public List<String> getContacts(String contactType) {
if (!this.contacts.containsKey(contactType)) {
this.contacts.put(contactType, new ArrayList<>());
}
return contacts.get(contactType);
}
}

spammer

spammer/SocialSpammer.java: 消息发送应用

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
26
27
28
29
30
31
32
33
34
35
36
package refactoring_guru.iterator.example.spammer;

import refactoring_guru.iterator.example.iterators.ProfileIterator;
import refactoring_guru.iterator.example.profile.Profile;
import refactoring_guru.iterator.example.social_networks.SocialNetwork;

public class SocialSpammer {
public SocialNetwork network;
public ProfileIterator iterator;

public SocialSpammer(SocialNetwork network) {
this.network = network;
}

public void sendSpamToFriends(String profileEmail, String message) {
System.out.println("\nIterating over friends...\n");
iterator = network.createFriendsIterator(profileEmail);
while (iterator.hasNext()) {
Profile profile = iterator.getNext();
sendMessage(profile.getEmail(), message);
}
}

public void sendSpamToCoworkers(String profileEmail, String message) {
System.out.println("\nIterating over coworkers...\n");
iterator = network.createCoworkersIterator(profileEmail);
while (iterator.hasNext()) {
Profile profile = iterator.getNext();
sendMessage(profile.getEmail(), message);
}
}

public void sendMessage(String email, String message) {
System.out.println("Sent message to: '" + email + "'. Message body: '" + message + "'");
}
}

Demo.java: 客户端代码

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package refactoring_guru.iterator.example;

import refactoring_guru.iterator.example.profile.Profile;
import refactoring_guru.iterator.example.social_networks.Facebook;
import refactoring_guru.iterator.example.social_networks.LinkedIn;
import refactoring_guru.iterator.example.social_networks.SocialNetwork;
import refactoring_guru.iterator.example.spammer.SocialSpammer;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

/**
* Demo class. Everything comes together here.
*/
public class Demo {
public static Scanner scanner = new Scanner(System.in);

public static void main(String[] args) {
System.out.println("Please specify social network to target spam tool (default:Facebook):");
System.out.println("1. Facebook");
System.out.println("2. LinkedIn");
String choice = scanner.nextLine();

SocialNetwork network;
if (choice.equals("2")) {
network = new LinkedIn(createTestProfiles());
}
else {
network = new Facebook(createTestProfiles());
}

SocialSpammer spammer = new SocialSpammer(network);
spammer.sendSpamToFriends("anna.smith@bing.com",
"Hey! This is Anna's friend Josh. Can you do me a favor and like this post [link]?");
spammer.sendSpamToCoworkers("anna.smith@bing.com",
"Hey! This is Anna's boss Jason. Anna told me you would be interested in [link].");
}

public static List<Profile> createTestProfiles() {
List<Profile> data = new ArrayList<Profile>();
data.add(new Profile("anna.smith@bing.com", "Anna Smith", "friends:mad_max@ya.com", "friends:catwoman@yahoo.com", "coworkers:sam@amazon.com"));
data.add(new Profile("mad_max@ya.com", "Maximilian", "friends:anna.smith@bing.com", "coworkers:sam@amazon.com"));
data.add(new Profile("bill@microsoft.eu", "Billie", "coworkers:avanger@ukr.net"));
data.add(new Profile("avanger@ukr.net", "John Day", "coworkers:bill@microsoft.eu"));
data.add(new Profile("sam@amazon.com", "Sam Kitting", "coworkers:anna.smith@bing.com", "coworkers:mad_max@ya.com", "friends:catwoman@yahoo.com"));
data.add(new Profile("catwoman@yahoo.com", "Liza", "friends:anna.smith@bing.com", "friends:sam@amazon.com"));
return data;
}
}

OutputDemo.png: 执行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Please specify social network to target spam tool (default:Facebook):
1. Facebook
2. LinkedIn
> 1

Iterating over friends...

Facebook: Loading 'friends' list of 'anna.smith@bing.com' over the network...
Facebook: Loading profile 'mad_max@ya.com' over the network...
Sent message to: 'mad_max@ya.com'. Message body: 'Hey! This is Anna's friend Josh. Can you do me a favor and like this post [link]?'
Facebook: Loading profile 'catwoman@yahoo.com' over the network...
Sent message to: 'catwoman@yahoo.com'. Message body: 'Hey! This is Anna's friend Josh. Can you do me a favor and like this post [link]?'

Iterating over coworkers...

Facebook: Loading 'coworkers' list of 'anna.smith@bing.com' over the network...
Facebook: Loading profile 'sam@amazon.com' over the network...
Sent message to: 'sam@amazon.com'. Message body: 'Hey! This is Anna's boss Jason. Anna told me you would be interested in [link].'

在 PHP 中使用模式

复杂度: ★★☆

流行度: ★★★

使用示例: 该模式在 PHP 代码中很常见。 许多框架和程序库都会使用它来提供遍历其集合的标准方式。

PHP 拥有内置的迭代器接口, 可用于创建与其他 PHP 代码兼容的自定义迭代器。

识别方法: 迭代器可以通过导航方法 (例如 nextprevious等) 来轻松识别。 使用迭代器的客户端代码可能没有其所遍历的集合的直接访问权限。

概念示例

本例说明了迭代器设计模式的结构并重点回答了下面的问题:

  • 它由哪些类组成?
  • 这些类扮演了哪些角色?
  • 模式中的各个元素会以何种方式相互关联?

了解该模式的结构后, 你可以更容易地理解下面基于真实世界的 PHP 应用案例。

index.php: 概念示例

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
<?php

namespace RefactoringGuru\Iterator\Conceptual;

/**
* Concrete Iterators implement various traversal algorithms. These classes
* store the current traversal position at all times.
*/
class AlphabeticalOrderIterator implements \Iterator
{
/**
* @var WordsCollection
*/
private $collection;

/**
* @var int 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 = 0;

/**
* @var bool This variable indicates the traversal direction.
*/
private $reverse = false;

public function __construct($collection, $reverse = false)
{
$this->collection = $collection;
$this->reverse = $reverse;
}

public function rewind()
{
$this->position = $this->reverse ?
count($this->collection->getItems()) - 1 : 0;
}

public function current()
{
return $this->collection->getItems()[$this->position];
}

public function key()
{
return $this->position;
}

public function next()
{
$this->position = $this->position + ($this->reverse ? -1 : 1);
}

public function valid()
{
return isset($this->collection->getItems()[$this->position]);
}
}

/**
* Concrete Collections provide one or several methods for retrieving fresh
* iterator instances, compatible with the collection class.
*/
class WordsCollection implements \IteratorAggregate
{
private $items = [];

public function getItems()
{
return $this->items;
}

public function addItem($item)
{
$this->items[] = $item;
}

public function getIterator(): Iterator
{
return new AlphabeticalOrderIterator($this);
}

public function getReverseIterator(): Iterator
{
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.
*/
$collection = new WordsCollection();
$collection->addItem("First");
$collection->addItem("Second");
$collection->addItem("Third");

echo "Straight traversal:\n";
foreach ($collection->getIterator() as $item) {
echo $item . "\n";
}

echo "\n";
echo "Reverse traversal:\n";
foreach ($collection->getReverseIterator() as $item) {
echo $item . "\n";
}

Output.txt: 执行结果

1
2
3
4
5
6
7
8
9
Straight traversal:
First
Second
Third

Reverse traversal:
Third
Second
First

真实世界示例

由于 PHP 已有内置的迭代器接口并能方便地与 foreach 循环整合, 因此你很轻松就能为任何数据结构创建你自己的迭代器。

该迭代器模式示例可以轻松地访问 CSV 文件。

index.php: 真实世界示例

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
<?php

namespace RefactoringGuru\Iterator\RealWorld;

/**
* CSV File Iterator.
*
* @author Josh Lockhart
*/
class CsvIterator implements \Iterator
{
const ROW_SIZE = 4096;

/**
* The pointer to the CSV file.
*
* @var resource
*/
protected $filePointer = null;

/**
* The current element, which is returned on each iteration.
*
* @var array
*/
protected $currentElement = null;

/**
* The row counter.
*
* @var int
*/
protected $rowCounter = null;

/**
* The delimiter for the CSV file.
*
* @var string
*/
protected $delimiter = null;

/**
* The constructor tries to open the CSV file. It throws an exception on
* failure.
*
* @param string $file The CSV file.
* @param string $delimiter The delimiter.
*
* @throws \Exception
*/
public function __construct($file, $delimiter = ',')
{
try {
$this->filePointer = fopen($file, 'rb');
$this->delimiter = $delimiter;
} catch (\Exception $e) {
throw new \Exception('The file "' . $file . '" cannot be read.');
}
}

/**
* This method resets the file pointer.
*/
public function rewind(): void
{
$this->rowCounter = 0;
rewind($this->filePointer);
}

/**
* This method returns the current CSV row as a 2-dimensional array.
*
* @return array The current CSV row as a 2-dimensional array.
*/
public function current(): array
{
$this->currentElement = fgetcsv($this->filePointer, self::ROW_SIZE, $this->delimiter);
$this->rowCounter++;

return $this->currentElement;
}

/**
* This method returns the current row number.
*
* @return int The current row number.
*/
public function key(): int
{
return $this->rowCounter;
}

/**
* This method checks if the end of file has been reached.
*
* @return bool Returns true on EOF reached, false otherwise.
*/
public function next(): bool
{
if (is_resource($this->filePointer)) {
return !feof($this->filePointer);
}

return false;
}

/**
* This method checks if the next row is a valid row.
*
* @return bool If the next row is a valid row.
*/
public function valid(): bool
{
if (!$this->next()) {
if (is_resource($this->filePointer)) {
fclose($this->filePointer);
}

return false;
}

return true;
}
}

/**
* The client code.
*/
$csv = new CsvIterator(__DIR__ . '/cats.csv');

foreach ($csv as $key => $row) {
print_r($row);
}

Output.txt: 执行结果

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
Array
(
[0] => Name
[1] => Age
[2] => Owner
[3] => Breed
[4] => Image
[5] => Color
[6] => Texture
[7] => Fur
[8] => Size
)
Array
(
[0] => Steve
[1] => 3
[2] => Alexander Shvets
[3] => Bengal
[4] => /cats/bengal.jpg
[5] => Brown
[6] => Stripes
[7] => Short
[8] => Medium
)
Array
(
[0] => Siri
[1] => 2
[2] => Alexander Shvets
[3] => Domestic short-haired
[4] => /cats/domestic-sh.jpg
[5] => Black
[6] => Solid
[7] => Medium
[8] => Medium
)
Array
(
[0] => Fluffy
[1] => 5
[2] => John Smith
[3] => Maine Coon
[4] => /cats/Maine-Coon.jpg
[5] => Gray
[6] => Stripes
[7] => Long
[8] => Large
)

在 Python 中使用模式

复杂度: ★★☆

流行度: ★★★

使用示例: 该模式在 Python 代码中很常见。 许多框架和程序库都使用它来提供遍历其集合的标准方式。

识别方法: 迭代器可以通过导航方法 (例如 nextprevious等) 来轻松识别。 使用迭代器的客户端代码可能没有其所遍历的集合的直接访问权限。

概念示例

本例说明了迭代器设计模式的结构并重点回答了下面的问题:

  • 它由哪些类组成?
  • 这些类扮演了哪些角色?
  • 模式中的各个元素会以何种方式相互关联?

main.py: 概念示例

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
from __future__ import annotations
from collections.abc import Iterable, Iterator
from typing import Any, List


"""
To create an iterator in Python, there are two abstract classes from the built-
in `collections` module - Iterable,Iterator. We need to implement the
`__iter__()` method in the iterated object (collection), and the `__next__ ()`
method in theiterator.
"""


class AlphabeticalOrderIterator(Iterator):
"""
Concrete Iterators implement various traversal algorithms. These classes
store the current traversal position at all times.
"""

"""
`_position` attribute 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.
"""
_position: int = None

"""
This attribute indicates the traversal direction.
"""
_reverse: bool = False

def __init__(self, collection: WordsCollection, reverse: bool = False) -> None:
self._collection = collection
self._reverse = reverse
self._position = -1 if reverse else 0

def __next__(self):
"""
The __next__() method must return the next item in the sequence. On
reaching the end, and in subsequent calls, it must raise StopIteration.
"""
try:
value = self._collection[self._position]
self._position += -1 if self._reverse else 1
except IndexError:
raise StopIteration()

return value


class WordsCollection(Iterable):
"""
Concrete Collections provide one or several methods for retrieving fresh
iterator instances, compatible with the collection class.
"""

def __init__(self, collection: List[Any] = []) -> None:
self._collection = collection

def __iter__(self) -> AlphabeticalOrderIterator:
"""
The __iter__() method returns the iterator object itself, by default we
return the iterator in ascending order.
"""
return AlphabeticalOrderIterator(self._collection)

def get_reverse_iterator(self) -> AlphabeticalOrderIterator:
return AlphabeticalOrderIterator(self._collection, True)

def add_item(self, item: Any):
self._collection.append(item)


if __name__ == "__main__":
# 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.
collection = WordsCollection()
collection.add_item("First")
collection.add_item("Second")
collection.add_item("Third")

print("Straight traversal:")
print("\n".join(collection))
print("")

print("Reverse traversal:")
print("\n".join(collection.get_reverse_iterator()), end="")

Output.txt: 执行结果

1
2
3
4
5
6
7
8
9
Straight traversal:
First
Second
Third

Reverse traversal:
Third
Second
First

在 Ruby 中使用模式

复杂度: ★★☆

流行度: ★★★

使用示例: 该模式在 Ruby 代码中很常见。 许多框架和程序库都使用它来提供遍历其集合的标准方式。

识别方法: 迭代器可以通过导航方法 (例如 nextprevious等) 来轻松识别。 使用迭代器的客户端代码可能没有其所遍历的集合的直接访问权限。

概念示例

本例说明了迭代器设计模式的结构并重点回答了下面的问题:

  • 它由哪些类组成?
  • 这些类扮演了哪些角色?
  • 模式中的各个元素会以何种方式相互关联?

main.rb: 概念示例

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
class AlphabeticalOrderIterator
# In Ruby, the Enumerable mixin provides classes with several traversal and
# searching methods, and with the ability to sort. The class must provide a
# method each, which yields successive members of the collection.
include Enumerable

# This attribute indicates the traversal direction.
attr_accessor :reverse
private :reverse

# @return [Array]
attr_accessor :collection
private :collection

# @param [Array] collection
# @param [Boolean] reverse
def initialize(collection, reverse = false)
@collection = collection
@reverse = reverse
end

def each(&block)
return @collection.reverse.each(&block) if reverse

@collection.each(&block)
end
end

class WordsCollection
# @return [Array]
attr_accessor :collection
private :collection

def initialize(collection = [])
@collection = collection
end

# The `iterator` method returns the iterator object itself, by default we
# return the iterator in ascending order.
def iterator
AlphabeticalOrderIterator.new(@collection)
end

# @return [AlphabeticalOrderIterator]
def reverse_iterator
AlphabeticalOrderIterator.new(@collection, true)
end

# @param [String] item
def add_item(item)
@collection << item
end
end

# 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.
collection = WordsCollection.new
collection.add_item('First')
collection.add_item('Second')
collection.add_item('Third')

puts 'Straight traversal:'
collection.iterator.each { |item| puts item }
puts "\n"

puts 'Reverse traversal:'
collection.reverse_iterator.each { |item| puts item }

output.txt: 执行结果

1
2
3
4
5
6
7
8
9
Straight traversal:
First
Second
Third

Reverse traversal:
Third
Second
First

在 Swift 中使用模式

复杂度: ★★☆

流行度: ★★★

使用示例: 该模式在 Swift 代码中很常见。 许多框架和程序库都使用它来提供遍历其集合的标准方式。

识别方法: 迭代器可以通过导航方法 (例如 nextprevious等) 来轻松识别。 使用迭代器的客户端代码可能没有其所遍历的集合的直接访问权限。

概念示例

本例说明了迭代器设计模式的结构并重点回答了下面的问题:

  • 它由哪些类组成?
  • 这些类扮演了哪些角色?
  • 模式中的各个元素会以何种方式相互关联?

了解该模式的结构后, 你可以更容易地理解下面基于真实世界的 Swift 应用案例。

Example.swift: 概念示例

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import XCTest

/// This is a collection that we're going to iterate through using an iterator
/// derived from IteratorProtocol.
class WordsCollection {

fileprivate lazy var items = [String]()

func append(_ item: String) {
self.items.append(item)
}
}

extension WordsCollection: Sequence {

func makeIterator() -> WordsIterator {
return WordsIterator(self)
}
}

/// Concrete Iterators implement various traversal algorithms. These classes
/// store the current traversal position at all times.
class WordsIterator: IteratorProtocol {

private let collection: WordsCollection
private var index = 0

init(_ collection: WordsCollection) {
self.collection = collection
}

func next() -> String? {
defer { index += 1 }
return index < collection.items.count ? collection.items[index] : nil
}
}


/// This is another collection that we'll provide AnyIterator for traversing its
/// items.
class NumbersCollection {

fileprivate lazy var items = [Int]()

func append(_ item: Int) {
self.items.append(item)
}
}

extension NumbersCollection: Sequence {

func makeIterator() -> AnyIterator<Int> {
var index = self.items.count - 1

return AnyIterator {
defer { index -= 1 }
return index >= 0 ? self.items[index] : nil
}
}
}

/// Client does not know the internal representation of a given sequence.
class Client {
// ...
static func clientCode<S: Sequence>(sequence: S) {
for item in sequence {
print(item)
}
}
// ...
}

/// Let's see how it all works together.
class IteratorConceptual: XCTestCase {

func testIteratorProtocol() {

let words = WordsCollection()
words.append("First")
words.append("Second")
words.append("Third")

print("Straight traversal using IteratorProtocol:")
Client.clientCode(sequence: words)
}

func testAnyIterator() {

let numbers = NumbersCollection()
numbers.append(1)
numbers.append(2)
numbers.append(3)

print("\nReverse traversal using AnyIterator:")
Client.clientCode(sequence: numbers)
}
}

Output.txt: 执行结果

1
2
3
4
5
6
7
8
9
Straight traversal using IteratorProtocol:
First
Second
Third

Reverse traversal using AnyIterator:
3
2
1

真实世界示例

Example.swift: 真实世界示例

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import XCTest

class IteratorRealWorld: XCTestCase {

func test() {

let tree = Tree(1)
tree.left = Tree(2)
tree.right = Tree(3)

print("Tree traversal: Inorder")
clientCode(iterator: tree.iterator(.inOrder))

print("\nTree traversal: Preorder")
clientCode(iterator: tree.iterator(.preOrder))

print("\nTree traversal: Postorder")
clientCode(iterator: tree.iterator(.postOrder))
}

func clientCode<T>(iterator: AnyIterator<T>) {
while case let item? = iterator.next() {
print(item)
}
}
}

class Tree<T> {

var value: T
var left: Tree<T>?
var right: Tree<T>?

init(_ value: T) {
self.value = value
}

typealias Block = (T) -> ()

enum IterationType {
case inOrder
case preOrder
case postOrder
}

func iterator(_ type: IterationType) -> AnyIterator<T> {
var items = [T]()
switch type {
case .inOrder:
inOrder { items.append($0) }
case .preOrder:
preOrder { items.append($0) }
case .postOrder:
postOrder { items.append($0) }
}

/// Note:
/// AnyIterator is used to hide the type signature of an internal
/// iterator.
return AnyIterator(items.makeIterator())
}

private func inOrder(_ body: Block) {
left?.inOrder(body)
body(value)
right?.inOrder(body)
}

private func preOrder(_ body: Block) {
body(value)
left?.inOrder(body)
right?.inOrder(body)
}

private func postOrder(_ body: Block) {
left?.inOrder(body)
right?.inOrder(body)
body(value)
}
}

Output.txt: 执行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Tree traversal: Inorder
2
1
3

Tree traversal: Preorder
1
2
3

Tree traversal: Postorder
2
3
1

在 TypeScript 中使用模式

复杂度: ★★☆

流行度: ★★★

使用示例: 该模式在 TypeScript 代码中很常见。 许多框架和程序库都使用它来提供遍历其集合的标准方式。

识别方法: 迭代器可以通过导航方法 (例如 nextprevious等) 来轻松识别。 使用迭代器的客户端代码可能没有其所遍历的集合的直接访问权限。

概念示例

本例说明了迭代器设计模式的结构并重点回答了下面的问题:

  • 它由哪些类组成?
  • 这些类扮演了哪些角色?
  • 模式中的各个元素会以何种方式相互关联?

index.ts: 概念示例

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/**
* 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());
}

Output.txt: 执行结果

1
2
3
4
5
6
7
8
9
Straight traversal:
First
Second
Third

Reverse traversal:
Third
Second
First

概念示例

迭代器模式的主要思想是将集合背后的迭代逻辑提取至不同的、 名为迭代器的对象中。 此迭代器提供了一种泛型方法, 可用于在集合上进行迭代, 而又不受其类型影响。

collection.go: 集合

1
2
3
4
5
package main

type collection interface {
createIterator() iterator
}

userCollection.go: 具体集合

1
2
3
4
5
6
7
8
9
10
11
package main

type userCollection struct {
users []*user
}

func (u *userCollection) createIterator() iterator {
return &userIterator{
users: u.users,
}
}

iterator.go: 迭代器

1
2
3
4
5
6
package main

type iterator interface {
hasNext() bool
getNext() *user
}

userIterator.go: 具体迭代器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

type userIterator struct {
index int
users []*user
}

func (u *userIterator) hasNext() bool {
if u.index < len(u.users) {
return true
}
return false

}
func (u *userIterator) getNext() *user {
if u.hasNext() {
user := u.users[u.index]
u.index++
return user
}
return nil
}

user.go: 客户端代码

1
2
3
4
5
6
package main

type user struct {
name string
age int
}

main.go: 客户端代码

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
26
package main

import "fmt"

func main() {

user1 := &user{
name: "a",
age: 30,
}
user2 := &user{
name: "b",
age: 20,
}

userCollection := &userCollection{
users: []*user{user1, user2},
}

iterator := userCollection.createIterator()

for iterator.hasNext() {
user := iterator.getNext()
fmt.Printf("User is %+v\n", user)
}
}

output.txt: 执行结果

1
2
User is &{name:a age:30}
User is &{name:b age:20}

根据: Golang By Example