前言:这两天学习 Iterator 模式,不知道是吕震宇老师没写这一篇,还是我没找到,就想到了把自己的学习体会拿出来与大家分享,我也是初学者,希望大家能提出意见。另外转载请注明作者和出处,毕竟花了快一周的时间才完成。
一、两个概念
1、聚集:所谓聚集就是一组数据集或者对象集,它可以通过循环来访问 。
2、枚举器: 专门用来访问聚集的类,他包装了一定的方法,可以依次把聚集中的数据按照一定的顺序读出来。
二、枚举器模式中出现的接口和类
1、Aggregate 接口:抽象的聚集,通常只留有一个方法让子类去实现,这个方法的作用是获得一个枚举器对象,通常可以起名字为 GetIterator( ),CreateIterator( ) 等等,或者干脆叫做 Iterator( ) 也行。在 C# 中,这个方法的名字叫做 GetEnumerator( ) ,这个我们到后面再讲。当然,在获得枚举器的同时,也要把自己 this(一组数据集)当作参数传给枚举器,想想也是,如果没有数据集,枚举器去枚举什么呢?
2、ConcreteAggregate 类:具体的聚集,它的实例保存了一组数据集,同时它实现了 Aggregate 接口中唯一的那个方法,通常情况下他也会扩展出一些其他方法便于访问聚集中的数据,常见的有:访问某个位置数据的方法,可以叫做 GetElement(int index) 或者 GetItem(int index) 等等;获得聚集大小的方法,可以起名字为 GetLength( ) 或者 GetSize( ) 等等,全看自己喜好。
3、Iterator 接口:抽象的枚举器,通常情况下会有三个方法留给子类去实现,他们分别是:Next( ) ,用来把指针移动到聚集中的下一个数据;HasNext( ) ,用来判断是否还有下一个数据;CurrentItem( ),返回当前指针所指位置的数据。也可以把 Next( ) 和 CurrentItem( ) 组成一个方法,在移动指针的同时返回一个数据。也可以有其他的实现方式,或者简单,或者复杂,也是全看个人需求。
4、ConcreteIterator 类:具体的枚举器,它实现了上述的三个方法,通过不同的实现方式,我们可以获得不同的枚举方式,如顺序枚举、倒序枚举等等。当然,这个类的构造方法中会接受一个具体聚集参数,想想也是,如果没有这个数据集,他去枚举什么呢?这个参数就是他要去枚举的对象。
5、被聚集的类,这个可以是任何类,它的许多个对象被存到聚集对象中才能形成一个真正的聚集,在我的例子中用的类叫 Book 类,它的四个实例即四本书形成一个聚集。下面看具体的代码吧。
三、常规枚举器模式的代码实现
这里用到“常规”这个词是指,这里的代码可以当作一个模版来用,不管你用的是 C# 还是 Java 或者是 Delphi ,均可以按照这个模版来实现枚举器模式。当然在 C# 和 Java 中,因为它们本身就支持枚举器模式,实现起来更方便一些,C# 的实现方法会在后面给出。
我把客户端代码和模式类代码分别放在两个文件中。
下面是模式类代码 Iterator.cs



















































































































下面是客户端代码:Client.cs

























四、在 C#中枚举器模式的实现——使用 foreach 语句
在 C# 中,因为 Aggregate 接口和 Iterator 接口,已经存在了,我们直接去实现他就可以了。 C# 中的Aggregate 接口叫做 IEnumerable,翻译过来是可以被枚举的,什么可以被枚举?当然就是聚集了。Iterator 接口叫做 IEnumerator ,是不是换汤不换药?如果上面的代码您已经看懂了,下面的代码应该也难不住你,我就不多说了,有注释。
我把客户端代码和模式类代码分别放在两个文件中。
下面是模式类代码 Iterator.cs
























































































下面是客户端代码:Client.cs















关于这两段代码,用分步运行就可以搞清楚 foreach 是怎么运作的了。但我还是要解释一下。
1、首先 in 后面的变量一定得是个聚集,即必须返回一个 IEnumerable 类型的对象。在这里我们直接给它了,就是 agg 。但当你想实现多种枚举方式的时候,就必须知道这一点。所以再重复一遍,in 后面的变量必须是一个 IEnumerable 类型的对象。
2、当循环开始启动时, 首先运行的是 in 后面那个聚集对象的 GetEnumerator( ) 方法,它返回一个枚举器,并把聚集自身当作参数传给枚举器。这个枚举器到底返回给谁了?我不知道,但是有一点我很清楚,这个方法 return 的是哪个枚举器的实例, foreach 就会用哪个枚举器的方式去枚举数据。当你想实现多种枚举方式的时候,你必须知道这一点。这里我们只用到了一个枚举器,后面我们会举一个两个枚举器的例子。
3、当确定使用哪个枚举器以后, foreach 接着会调用这个枚举器的 MoveNext( ) 方法,什么都没干先移动一下?很奇怪吧?就是这样!所以注意 index 要从 -1 开始。
4、每 MoveNext( ) 一下,接着就调用 Current 属性,返回当前指针指向的数据。
5、当 MoveNext( ) 返回 false 时,一个 foreach 过程就结束了。
运行效果如下图:
五、一个聚集实现多个枚举器——使用 foreach 语句
这一部分我不作讲解了,除了上面的功能外,多实现的一个倒序的枚举器,如果你能看懂上一部分的5点,这里的代码就很容易理解了。在C# 2.0中还有更好地实现方式,但是不适合用于讲解模式,有兴趣的朋友可以自己去查一些资料。另外总觉得这段代码应该不是很漂亮,有修改的余地,请高手赐教。
下面是模式类代码 Iterator.cs









































































































































































下面是客户端代码:Client.cs




















运行效果如下图: