迭代器模式

1、学院展示需求

编写程序展示一个学校院系结构:需求是这样,要在一个页面中展示出学校的院系组成,一个学校有多个学院,一个学院有多个系。

【传统方式问题分析】

1)将学院看作是学校的子类,系是学院的子类,这样实际上是站在组织大小来进行分层次的。

2)实际上我们的要求是:在一个页面中展示出学校的院系组成,一个学校有多个学院,一个学院有多个系,因此这种方案,不能很好实现遍历的操作。

3)解决方案:迭代器模式。


2、迭代器模式的基本介绍

1)迭代器模式(Iterator Pattern)是常用的设计模式,属于行为型模式。

2)如果我们的集合元素是用不同的方式实现的,有数组,还有java的集合类,或者还有其他方式,当客户端要遍历这些集合元素的时候就要使用多种遍历方式,而且还会暴露元素的内部结构,可以考虑使用迭代器模式解决。

3)迭代器模式,提供一种遍历集合元素的统一接口,用一致的方法遍历集合元素,不需要知道集合对象的底层表示,即:不暴露其内部的结构。


3、结构

  • 抽象聚合(Aggregate)角色:定义存储、添加、删除聚合元素以及创建迭代器对象的接口。
  • 具体聚合(ConcreteAggregate)角色:具体的聚合持有对象集合,并提供一个方法,返回一个迭代器。
  • 抽象迭代器(Iterator)角色:定义访问和遍历聚合元素的接口,通常含有hasNext、next、remove等方法。
  • 具体迭代器(ConcreteIterator)角色:实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置。

4、应用实例代码实现

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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
// 系
public class Department {
private String name;
private String desc;
public Department(String name, String desc) {
this.name = name;
this.desc = desc;
}
getter、setter
}

public class ComputerCollegeIterator implements Iterator {
// 这里我们需要Department是以怎样的方式存放
private Department[] departments;
// 遍历的位置
private int position = 0;

public ComputerCollegeIterator(Department[] departments) {
this.departments = departments;
}

@Override
public boolean hasNext() {
return position != departments.length;
}

@Override
public Object next() {
Department department = departments[position];
position++;
return department;
}

public void remove() {

}
}

public class InfoCollegeIterator implements Iterator {

private List<Department> departments;
int index = 0;

public InfoCollegeIterator(List departments) {
this.departments = departments;
}

@Override
public boolean hasNext() {
return index != departments.size();
}

@Override
public Object next() {
Department department = departments.get(index);
index++;
return department;
}

public void remove() {

}
}

public interface College {
String getName();

// 增加系的方法
void addDepartment(String name, String desc);

// 返回一个迭代器,遍历
Iterator createIterator();
}

public class ComputerCollege implements College {
private Department[] departments;
private int numOfDepartment = 0; // 保存当前数组的对象个数

public ComputerCollege() {
departments = new Department[5];
addDepartment("Java专业", "Java专业");
addDepartment("PHP专业", "PHP专业");
addDepartment("大数据专业", "大数据专业");
}

@Override
public String getName() {
return "计算机学院";
}

@Override
public void addDepartment(String name, String desc) {
Department department = new Department(name, desc);
departments[numOfDepartment] = department;
numOfDepartment++;
}

@Override
public Iterator createIterator() {
return new ComputerCollegeIterator(departments);
}
}

public class InfoCollege implements College {
private List<Department> departments;
private int numOfDepartment = 0; // 保存当前数组的对象个数

public InfoCollege() {
departments = new ArrayList();
addDepartment("信息安全专业", "信息安全专业");
addDepartment("网络安全", "网络安全");
addDepartment("服务器安全", "服务器安全");
}

@Override
public String getName() {
return "信息工程学院";
}

@Override
public void addDepartment(String name, String desc) {
Department department = new Department(name, desc);
departments.add(department);
numOfDepartment++;
}

@Override
public Iterator createIterator() {
return new InfoCollegeIterator(departments);
}
}

public class OutputImpl {
// 学院集合
List<College> collegeList;

public OutputImpl(List<College> collegeList) {
this.collegeList = collegeList;
}

// 遍历所有学院,调用printDepartment输出各个学院的系
public void printCollege() {
collegeList.forEach(college -> {
System.out.println("====" + college.getName() + "====");
Iterator iterator = college.createIterator();
printDepartment(iterator);
});
}

// 输出系
public void printDepartment(Iterator iterator) {
while(iterator.hasNext()) {
Department d = (Department)iterator.next();
System.out.println(d.getName());
}
}
}

public class Client {
public static void main(String[] args) {
List<College> collegeList = new ArrayList();

ComputerCollege computerCollge = new ComputerCollege();
InfoCollege infoCollege = new InfoCollege();

collegeList.add(computerCollge);
collegeList.add(infoCollege);

OutputImpl op = new OutputImpl(collegeList);
op.printCollege();
}
}

5、迭代器模式的注意事项

【优点】

1)提供一个统一的方法遍历对象,客户不用再考虑聚合的类型,使用一种方法就可以遍历对象了。同时它支持以不同的方式遍历一个聚合对象,在同一个聚合对象上可以定义多种遍历方式。在迭代器模式中只需要用一个不同的迭代器来替换原有迭代器即可改变遍历算法,我们也可以自己定义迭代器的子类以支持新的遍历方式。

2)隐藏了聚合的内部结构,客户端要遍历聚合的时候只需取到迭代器,而不会知道聚合的具体组成。

3)提供了一种设计思想,就是一个类应该只有一个引起变化的原因(叫做单一职责原则)。在聚合类中,我们把迭代器分开,就是要把管理对象集合和遍历对象集合的责任分开,这样一来集合改变的话,只影响到聚合对象。而如果遍历方式改变的话,只影响到了迭代器。

4)当要展示一组相似对象,或者遍历一组相同对象时,适合使用迭代器模式。

5)在迭代器模式中,由于引入了抽象层,增加新的聚合类和迭代器类都很方便,无需修改原有代码,满足”开闭原则”的要求。

【缺点】

1)每个聚合对象都要一个迭代器,会生成多个迭代器,不好管理。

【使用场景】

1)当需要为聚合对象提供多种遍历方式时。

2)当需要为遍历不同的聚合结构提供一个统一的接口时。

3)当访问一个聚合对象的内容而无须暴露其内部细节的表示时。

注意:

当我们在使用Java开发的时候,想使用迭代器模式的话,只要让我们自己定义的容器类实现java.util.Iterable并实现其中的iterator()方法使其返回一个java.util.Iterator的实现类就可以的。


6、迭代器模式在JDK-ArrayList集合应用的源码分析

1)JDK的ArrayList集合中就使用了迭代器模式。

2)代码分析。

  • 内部类Itr充当具体实现迭代器Iterator的类,作为ArrayList内部类。
  • List就是充当了聚合接口,含有一个iterator()方法,返回一个迭代器对象。
  • ArrayList是实现聚合接口List的子类,实现了iterator()。
  • Iterator接口系统提供。
  • 迭代器模式解决了不同集合(ArrayList,LinkedList)统一遍历问题。