我是荔园微风,作为一名在IT界整整25年的老兵,今天总结一下Windows环境下如何编程实现迭代器模式(设计模式)。
不知道大家有没有这样的感觉,看了一大堆编程和设计模式的书,却还是很难理解设计模式,无从下手。为什么?因为你看的都是理论书籍。
我今天就在Windows操作系统上安装好JAVA的IDE编程工具,并用JAVA语言来实现一个迭代器模式,真实的实现一个,你看懂代码后,自然就明白了。
迭代器模式Iterator Pattern (行为型设计模式)
定义:提供一种方法来访问聚合对象,而不用暴露这个对象的内部表示。
上面定义听懂了吗?莫名其妙看不懂对吧。所以我们还是来看看实现生活中的例子。
现在有一群小朋友排队去春游,如果大家都个管个的排队,每个小朋友都玩自己的,那我问下图,小朋友们分成几个组?
你肯定可以很轻松的告诉我,一共分12组,一个小朋友一组。没错。然后我让你给所有组点个到,你会按顺序把所有小朋友都看一遍,看他们是否都来了。
那么现在变一变,让小朋友自己两两找玩伴,每两个小朋友手拉手在一起玩,这算几个组?
你肯定可以很轻松的告诉我,一共分6个组,两个小朋友一组。然后我让你给所有组点个到,你会按顺序把所有两人小组都看一遍,看他们是否都来了。
那么现在再变一变,让小朋友自己每六位自己找玩伴,每六个小朋友手拉手在一起玩,这算几个组?
你也肯定可以很轻松的告诉我,一共分2个组,六个小朋友一组。然后我让你给所有组点个到,你会按顺序把所有六人小组都看一遍,看他们是否都来了。
大家看到了吗?不管每个组内部如何组合,我们都能把他们都分成一个一个组,并且都点一遍名,来看看他们到了没有,这种模式就是迭代器。我个人还是认为这个名字没有起好,至少直接看看不出来什么意思。
不过,对于经常进行JAVA编程的同学来说,就很好理解了,因为你们经常用 Iterator,你们通过循环来遍历一些结构。这种模式使用频率很高。迭代器用于对一个聚合对象进行遍历。通过引入迭代器可以将数据的遍历功能从聚合对象中分离出来。聚合对象只负责存储数据,而遍历数据由迭代器完成。
在迭代器模式结构中包含聚合和迭代器两个层次结构,考虑到系统的灵活性和可扩展性,在迭代器模式中应用了工厂方法模式,其结构如图所示:
在迭代器模式结构图中包含如下几个角色:Iterator(抽象迭代器):定义了访问和遍历元素的接口,声明了用于遍历数据元素的方法,例如:用于获取第一个元素的first()方法,用于访问下一个元素的next()方法,用于判断是否还有下一个元素的hasNext()方法,用于获取当前元素的currentItem()方法等,在具体迭代器中将实现这些方法。 ConcreteIterator(具体迭代器):实现了抽象迭代器接口,完成对聚合对象的遍历,同时在具体迭代器中通过游标来记录在聚合对象中所处的当前位置,在具体实现时,游标通常是一个表示位置的非负整数。Aggregate(抽象聚合类):用于存储和管理元素对象,声明一个createIterator()方法用于创建一个迭代器对象,充当抽象迭代器工厂角色。ConcreteAggregate(具体聚合类):实现了在抽象聚合类中声明的createIterator()方法,该方法返回一个与该具体聚合类对应的具体迭代器ConcreteIterator实例。
实例代码
在迭代器模式中,提供了一个外部的迭代器来对聚合对象进行访问和遍历,迭代器定义了一个访问该聚合元素的接口,并且可以跟踪当前遍历的元素。迭代器的引入,将使得对一个复杂聚合对象的操作变得简单。
在抽象迭代器中声明了用于遍历聚合对象中所存储元素的方法,代码如下所示:
public interface Iterator {
public void first(); //将游标指向第一个元素
public void next(); //将游标指向下一个元素
public boolean hasNext(); //判断是否存在下一个元素
public Object currentItem(); //获取游标指向的当前元素
}
在具体迭代器中将实现抽象迭代器声明的遍历数据的方法,如下代码所示:
public class ConcreteIterator implements Iterator {
private ConcreteAggregate objects; //维持对聚合对象引用以便于访问存储在聚合对象中的数据
private int cursor; //定义一个游标,用于记录当前访问位置
public ConcreteIterator(ConcreteAggregate objects) {
this.objects=objects;
}
public void first() { ... }
public void next() { ... }
public boolean hasNext() { ... }
public Object currentItem() { ... }
}
抽象迭代器接口的设计非常重要,一方面需要充分满足各种遍历操作的要求,尽量为各种遍历方法都提供声明,另一方面又不能包含太多方法,方法太多将给子类的实现带来麻烦。因此可以考虑使用抽象类来设计抽象迭代器,在抽象类中为每一个方法提供一个空的默认实现。
聚合类用于存储数据并负责创建迭代器对象,最简单的抽象聚合类代码如下所示:
public interface Aggregate {
Iterator createIterator();
}
具体聚合类作为抽象聚合类的子类,一方面负责存储数据,另一方面实现了在抽象聚合类中声明的工厂方法createIterator(),用于返回一个与该具体聚合类对应的具体迭代器对象,代码如下所示:
public class ConcreteAggregate implements Aggregate {
...
public Iterator createIterator() {
return new ConcreteIterator(this);
}
...
}
应用实例
我们可以总结一下,当访问一个聚合对象的内容而无须暴露它的内部表示。将聚合对象的访问与内部数据的存储分离,使得访问聚合对象时无须了解其内部实现细节。同时又需要为一个聚合对象提供多种遍历方式。为遍历不同的聚合结构提供一个统一的接口,在该接口的实现类中为不同的聚合结构提供不同的遍历方式,而客户端可以一致性地操作该接口。如果满足上述条件,就可以用迭代器模式。
来个实际例子,一个大型超市用一套管理系统对整个超市的人、设备、货品进行管理,该系统在运行的过程中,经常需要对系统中的工作人员数据、商品数据、客户数据等进行遍历。所以要进行设计,AbstractObjectList充当抽象聚合类,ProductList充当具体聚合类,AbstractIterator充当抽象迭代器,ProductIterator充当具体迭代器。完整代码如下所示:
(1)AbstractObjectList充当抽象聚合类
package designpattrens.iterator;
import java.util.*;
//抽象聚合类
public abstract class AbstractObjectList {
protected List<Object> objects = new ArrayList<Object>();
public AbstractObjectList(List<Object> objects) {
this.objects = objects;
}
public void addObject(Object obj) {
this.objects.add(obj);
}
public void removeObject(Object obj) {
this.objects.remove(obj);
}
public List<Object> getObjects() {
return this.objects;
}
//声明创建迭代器对象的抽象工厂方法
public abstract AbstractIterator createIterator();
}
(2)ProductList充当具体聚合类
package designpattrens.iterator;
import java.util.*;
public class ProductList extends AbstractObjectList {
public ProductList(List<Object> products) {
super(products);
}
//实现创建迭代器对象的具体工厂方法
public AbstractIterator createIterator() {
return new ProductIterator(this);
}
}
(3)AbstractIterator充当抽象迭代器
package designpattrens.iterator;
interface AbstractIterator {
public void next(); //移至下一个元素
public boolean isLast(); //判断是否为最后一个元素
public void previous(); //移至上一个元素
public boolean isFirst(); //判断是否为第一个元素
public Object getNextItem(); //获取下一个元素
public Object getPreviousItem(); //获取上一个元素
}
(4)ProductIterator充当具体迭代器
package designpattrens.iterator;
import java.util.*
public class ProductIterator implements AbstractIterator {
private List<Object> products;
private int cursor1; //定义一个游标,用于记录正向遍历的位置
private int cursor2; //定义一个游标,用于记录逆向遍历的位置
public ProductIterator(ProductList list) {
this.products = list.getObjects(); //获取集合对象
cursor1 = 0; //设置正向遍历游标的初始值
cursor2 = products.size() -1; //设置逆向遍历游标的初始值
}
public void next() {
if(cursor1 < products.size()) {
cursor1++;
}
}
public boolean isLast() {
return (cursor1 == products.size());
}
public void previous() {
if (cursor2 > -1) {
cursor2--;
}
}
public boolean isFirst() {
return (cursor2 == -1);
}
public Object getNextItem() {
return products.get(cursor1);
}
public Object getPreviousItem() {
return products.get(cursor2);
}
}
(5)客户端代码
package designpattrens.iterator;
import java.util.*;
public class Client {
public static void main(String args[]) {
List<Object> products = new ArrayList<Object>();
products.add("货品1");
products.add("货品2");
products.add("货品3");
products.add("货品4");
products.add("货品5");
AbstractObjectList list;
AbstractIterator iterator;
list = new ProductList(products); //创建聚合对象
iterator = list.createIterator(); //创建迭代器对象
System.out.println("正向遍历:");
while(!iterator.isLast()) {
System.out.print(iterator.getNextItem() + ",");
iterator.next();
}
System.out.println();
System.out.println("逆向遍历:");
while(!iterator.isFirst()) {
System.out.print(iterator.getPreviousItem() + ",");
iterator.previous();
}
}
}
如果需要增加一个新的具体聚合类,如客户数据集合类,并且需要为客户数据集合类提供不同于商品数据集合类的正向遍历和逆向遍历操作,只需增加一个新的聚合子类和一个新的具体迭代器类即可,原有类库代码无须修改,符合“开闭原则”;如果需要为ProductList类更换一个迭代器,只需要增加一个新的具体迭代器类作为抽象迭代器类的子类,重新实现遍历方法,原有迭代器代码无须修改,也符合“开闭原则”。
由于很多编程语言的类库都已经实现了迭代器模式,因此在实际开发中,我们只需要直接使用Java、C#等语言已定义好的迭代器即可。
迭代器模式的主要优点如下:支持以不同的方式遍历一个聚合对象,在同一个聚合对象上可以定义多种遍历方式。在迭代器模式中只需要用一个不同的迭代器来替换原有迭代器即可改变遍历算法,也可以自己定义迭代器的子类以支持新的遍历方式。迭代器简化了聚合类。由于引入了迭代器,在原有的聚合对象中不需要再自行提供数据遍历等方法,这样可以简化聚合类的设计。在迭代器模式中,由于引入了抽象层,增加新的聚合类和迭代器类都很方便,无须修改原有代码。
迭代器模式的主要缺点如下:由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。抽象迭代器的设计难度较大,需要充分考虑到系统将来的扩展
各位小伙伴,这次我们就说到这里,下次我们再深入研究windows环境下的各类设计模式实现。
作者简介:荔园微风,1981年生,高级工程师,浙大工学硕士,软件工程项目主管,做过程序员、软件设计师、系统架构师,早期的Windows程序员,Visual Studio忠实用户,C/C++使用者,是一位在计算机界学习、拼搏、奋斗了25年的老将,经历了UNIX时代、桌面WIN32时代、Web应用时代、云计算时代、手机安卓时代、大数据时代、ICT时代、AI深度学习时代、智能机器时代,我不知道未来还会有什么时代,只记得这一路走来,充满着艰辛与收获,愿同大家一起走下去,充满希望的走下去。