C#中的模式---Iterator Pattern(上)

    在C#或者Java语言中都提供了一些机制来支持iterator模式.最近在看《Head First Design Pattern》,有了一点自己的想法,就把它给记录了下来。
假设我们有两个包含MenuItem集合的类,一个为PancakeHouseMenu,另一个为DinerMenu。他们的定义和实现分别如下:(注:大部分代码是从书中搬过来略作修改)
MenuItem:
Code
PancakeHouseMenu:
Code
DinerMenu:
Code
上面的代码相对比较简单,每个Menu类所做的事情就是提供一些基本的访问方法和属性。
现在,如果我们要加一个Waitress类来打印每个Menu里的项的话,可能就需要写成这样了。
Code
可以看到为了遍历每个集合,我们需要针对每个具体的集合实现使用一个for循环。这下子问题就来了,如果以后又有新的menu类来了呢?难道每次都往Waitress类里面加?这样子下下去肯定不行。
再仔细想想前面的代码,很重要的一部分在于我们是针对每个集合的具体实现来做循环的,也就是说集合里面的具体实现都暴露在客户端面前。而且,每次都要写一个循环,循环中的很多代码又是重复的,这可是一个很不好的设计。我们所期望的方式肯定是最好用一个统一的循环便利方式来访问每个集合类.比如说:
while(iterator.hasNext())
{
    MenuItem item = (MenuItem)iterator.next();
    //....
}
这样每次针对具体的集合只要调一个遍历的方法就可以了。
如果我们要达到这个目的,那么我们是不是可以提供一个接口定义hasNext和next等方法,然后由具体的类来实现,而在客户端只要针对这个接口进行变成就好了?
我们不妨定义一组如下的类:

这样在Waitress的PrintMenu方法可以引用Iterator接口作为参数,也分离了对实现的耦合。另外,我们定义的PancakeMenu和DinerMenu和Iterator之间又是什么关系呢?如何把他们关联起来?
其实,我们前面的代码还有一个问题就是每个类中间都有一个集合,这个类不但实现了自身的业务职能,还有一个遍历每个元素的职能。从OO的角度来说违背了单一职责的原则。如果我们把遍历每个元素的职责给剥离出来就可以保证了。所以说,每个Iterator的子类应该就是针对每个Menu类的遍历部分的具体实现,这样Menu类只要把自己的具体集合传给对应的Iterator子类,然后由他们去实现遍历,自己不用管。
这样我们整个实现的类图如下:

我们再回头看看这个设计,Waitress直接依赖于DinerMenu,PancakeMenu.对具体类的依赖似乎有悖于OO的原则,如果我们定义一个接口,让Waitress依赖于一个公用的接口会怎么样呢?回头看看两个Menu类,实际上都包含一个公有的部分,就是创建Iterator的方法。如此一来,我们可以将设计修改成如下:


下面是针对每个类的具体实现代码:
Menu:
Code
Iterator:
Code
DinerMenu:
Code
DinnerMenuIterator:
Code
PancakeMenu和PancakeMenuIterator的代码和DinerMenu的类似。
总结起来,Iterator模式的本质就是将业务职责和遍历的职责分离,实现带一职责原则。同时也注意针对接口编程。
说了这么多的Iterator Pattern。现在我们再来看看在C#中是如何应用到这个模式的。
我们知道,在C#中,如果要遍历一个类的每个成员的话,这个类要实现IEnumerable接口,同时还要提供一个IEnumerator接口的实现。这个IEnumerable接口就相当于我们前面定义的Menu接口,只是告诉具体的实现类给我一个Iterator的实现,也就是C#中的IEnumerator的实现。所以说,IEnumerator就相当于Iterator接口。
比如说如果我们有一个object[] values = {"a", "b", "c", "d", "e"} IterationSample collection = new IterationSample(values, 3);
foreach(object x in collection)
{
      Console.WriteLine(x);
}
执行这些代码将打印如下的信息:d e a b c
也就是说在遍历过程中根据指定的偏移量进行打印输出。按照C#中的方法,我们可以定义代码如下:
IterationSample:
Code
IterationSampleIterator:
Code
这样我们就实现了整个Iterator Pattern。当然,这种实现只是针对C# 1。对于后面的版本提供了哪些简化和便利,且看下回。
posted @ 2009-08-08 17:27  frankliu1980  阅读(984)  评论(0)    收藏  举报