1.引子
假如部门十个人组织一次秋游,由于人数较少所以计划集合后集体坐公交前往目的地。当日从起始站来了一辆空公交车,小伙伴上车坐好后,票务员来到每个人的身边开始收车票。这一个简单的场景就包含迭代模式。
场景内涉及到公交车、十个人的小集体还有票务员(司机看做公交车的一部分吧)。可以把场景抽象一下:
①公交车是一个聚集类(可容纳十个人的小集体还有一名票务员);
②十个人的小集体可以认为是一个长度为十的数组;
③票务员是自带有遍历整个公交内乘客的迭代类,它知道公交内坐了多少人,哪个座位坐了谁等。
用实例代码表示就是:
1.1 普通版
上车乘客信息:
public class Person{
private String name;
public String getName() {
return name;
}
public Person(String name){
this.name = name;
}
@Override
public String toString() {
return "Person.name=" + name;
}
}
注意:假设只需要名字就行,并通过构造函数注入乘客姓名;
公交类:
public class Bus {
private Person[] persons; // 上车乘客是一个数组
public Bus(Person[] persons){
this.persons = persons;
}
public Conductor createConductor(){
return new Conductor(this); // 售票员
}
// 获取上车人数
public int size(){
if (persons != null) {
return persons.length;
}else {
return 0;
}
}
// 获取车内index位置处乘客的信息
public Person getPerson(int index){
if (persons != null && persons.length > 0){
return persons[index];
} else {
return null;
}
}
}
注意:公交车聚集类有一个制造迭代器的工厂方法。
票务员类:
public class Conductor {
private Bus bus;
private int index; // 当前第index个顾客
private int size; // 总共有size个顾客
public Conductor(Bus bus){
this.bus = bus;
this.size = bus.size();
this.index = 0;
}
// 从第一个客户售票
public void first(){
index = 0;
}
// 下一个客户
public void next(){
index++;
}
// 是否最后一个客户
public boolean isLast(){
return index >= size;
}
// 获取当前位置的客户信息
public Person currentPerson(){
return bus.getPerson(index);
}
}
注意:票务员通过公交车的getPerson()方法获得乘客信息;通过first(),next(),isLast()方法遍历乘客;
测试类:
public class Main {
public static void main(String[] args) {
// 十位小伙伴
Person[] persons = new Person[10];
for (int i = 0; i < persons.length; i++) {
persons[i] = new Person("a"+i);
}
Bus bus = new Bus(persons); // 上车
Conductor conductor = bus.createConductor(); // 售票员
// 售票员遍历每一个乘客来收费
while (!conductor.isLast()) {
Person person = conductor.currentPerson();
conductor.next();
System.out.println(person);
}
}
}
运行结果:
Person.name=a0
Person.name=a1
Person.name=a2
Person.name=a3
Person.name=a4
Person.name=a5
Person.name=a6
Person.name=a7
Person.name=a8
Person.name=a9
假设该车的售票员小B今天生病了,临时换了另外一个售货员小C,在这个例子里面不仅需要新建小C类,还需要修改Bus类的createConductor()方法;同样,如果此辆公交车坏了,需要新换一辆公交车,即需要新建Bus类还需要修改Conductor类;违背了“开闭原则”。所以可以把公交车和售票员抽象出来,后期只需要扩展就行。
1.2 抽象版
抽象公交类:
public abstract class AbstractBus {
protected abstract AbstractConductor createConductor();
}
抽象售票员:
public interface AbstractConductor {
void first(); // 从第一个客户售票
void next(); // 下一个客户
boolean isLast(); // 是否最后一个客户
Person currentPerson(); // 获取当前位置的客户信息
}
实体公交类、实体售票员分别继承抽象公交类、实现抽象售票员接口;测试类基本不变。
类似于这种遍历方式的就是迭代模式。
2.迭代模式的原理
从上面的实际例子可知,公交车是一个聚集类;售票员是对公交车内Person属性进行遍历。在客户端中,我们并不知道公交车上有多少person,遍历完全由售票员来完成,进而隐藏了遍历的细节。
3.迭代模式的使用场景
当一个聚集对象需要被遍历,且被遍历的方式需要与聚集对象解耦的情况下,迭代器模式是不二之选。由于java语言本身带有众多集合类,如List、Set、Map等,这些集合内部都有迭代器,所以迭代模式很少用,除非自己定义一个容器。
4.迭代模式的特点
迭代器模式分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样做的好处就是既可以做到不暴露集合的内部结构,又可以让外部代码透明地访问集合内部的数据。
5.参考资料
https://blog.youkuaiyun.com/jason0539/article/details/45070441
《大话设计模式》