翻译:在我们使用的NET FRAMEWORK类库中发现设计模式(3)

迭代器模式(Iterator Pattern)

         许多编程的任务都是操作集合。无论是简单的对象列表还是复杂的B-Tree集合,我们都需要经常访问集合里的元素,我们经常通过一些方法来访问这些集合中的元素,例如从前到后,从后到前,或者正序或者倒序访问。
         一个最常见的基本方法之一就是将对象列表存储的数组里,数组类型已经内置在VB.NET与C#语言里了,它们都有对数组元素进行枚举的循环结构:在C#里是Foreach,在VB.NET里是For Eeah。这里有一个枚举数组的例子:
int[] values = new int[] {1, 2, 3, 4, 5};
foreach(int i in values)
{
   Console.Write(i.ToString() + " ");
}
这些语句会在内部使用一个迭代器,你所能知道的是确信能将数组中的元素在每一次循环中枚举出来。      
        为了让这些语句能正常执行,在表达式中数组对象必须实现IEnumerable,所有实现了IEnumerable对象集合类都能够遍历(也就是枚举),IEnumerable接口有一个方法:GetEnumerator,这个方法能返回一个实现了IEnumerator的类型,这个实现了IEnumerator的类型能够对这个集合进行枚举。它有一个Current的属性,并且有能够从头到尾的向前枚举集合中元素的方法—MoveNext和Rest,这些对象集合类都System.Collections名称空间里,这些集合类和数组一样,实现了IEnumerable接口凭此来枚举集合中的元素。 
        如果你查看一下C#编译器生成的使用Foreach语句的中间语言MSIL(Microsoft intermediate language),你会发现大多数的集合类都采用枚举器来进行枚举。下面有一个使用 IEnumerator方式来进行枚举的例子,它与前面的那个例子实现了同样的功能:
int[] values = new int[] {1, 2, 3, 4, 5};
IEnumerator e = ((IEnumerable)values).GetEnumerator();
while(e.MoveNext())
{
   
Console.Write(e.Current.ToString() + " ");
}
         .NET Framework使用IEnumerable和IEnumerator来应用迭代器模式,迭代器模式让你很轻松的遍历一个集合而不需要集合来展示自己内部的处理过程。实现了IEnumerator的迭代类从实现了IEnumerable的集合类中分离开来,这个类负责来处理枚举的状态(包括怎么表达当前数组中的元素和如何迭代集合中的元素),并把枚举的算法包含其中。这种方法能让你同时有几个用不同的方法枚举集合的迭代器,而不需要给集合添加任何的复杂性!

装饰模式(Decorator Pattern)

         有效的可执行程序不但包括读入与写出或者全部,无论读写的资源来源于哪里,资源都会被抽象的认作为字节流, .NET使用System.IO.Stream 类来表达这种抽象,不管它来源于文本文件,TCP/IP网络通信或者是其他的实体,文件数据类(FileStream)和网络通信类(NetworkStrem)都继承了Stream类,你可以轻松的处理这些数据而不管这些数据的来源,下面是一个将流中的字节输出到控制台的方法:

public static void PrintBytes(Stream s)

{

    int b;

    while((b = fs.ReadByte()) >= 0)

    {

        Console.Write(b + " ");

    }

}

       从流中一次读取一个字节不是一个非常有效的方式,例如:硬件能够(也是最适合的)连续的把数据块从磁盘中读到一个大的块中,如果你知道准备要读取一些字符,比较好的方法是将数据块一次性的从磁盘中取出后在内存中处理这些字符,在net Framework里,BufferedStream类负责处理这种操作,BufferedStream类的构造子有一个参数,这个参数可以是任何一个需要缓存处理的流对象, BufferedStream类重载了Stream的许多方法,比如Read和Write,凭此来提供更强大的功能(这个功能其实就是缓冲了,译者注)。因为 BufferedStream是Stream 类的子类,所以你可以像使用其它Stream类一样使用它。(值得注意的是FileStream已经内置了自己缓冲功能)
        同样的,你可以使用System.Security.Cryptography.CryptoStream类随意的对任何流对象进行加密和解密。下面的例子展示了使用几种不同类型的流对象来调用我的打印方法:

MemoryStream ms = new MemoryStream(new byte[] {1, 2, 3, 4, 5, 6, 7, 8});

PrintBytes(ms);

  BufferedStream buff = new BufferedStream(ms);

PrintBytes(buff);

buff.Close();

FileStream fs = new FileStream("../../decorator.txt", FileMode.Open);

PrintBytes(fs);

fs.Close();

这种通过组合方式无缝的,动态的给对象添加功能的能力就是装饰模式的一个例子。就像下图所展示的那样:

fig04.gif

         对于每一个Stream的实例,你可以通过将Stream包装成BufferedStream来给Stream来添加缓冲功能,而不需要改变数据的接口(就是Stream类型的接口,译者注),因为仅仅是组合对象(比如FileStream实例作为一个参数传给BufferedStream实例的构造子后,这个BufferedStream就是一个具有缓冲功能的FileStream实例,其他流实例也可以通过这种操作来扩展自己的功能,这就是装饰模式的精髓所在!译者注),这种方式比在编译时继承某一具体的流类型来得更加的优雅(比如,如果使用继承的方式,FileStream为了实现缓冲功能,而不得不有一个继承FileStream类的有缓冲功能的子类,这种方式的最大的问题是使整个继承树变得非常的臃肿,译者注),因为前一种方式可以在运行时实现!装饰模式核心的功能是所有的装饰者都实现了某接口或者重载了某一个抽象基类(比如Stream抽象基类)并提供了格外的功能。比如: BufferedStream重载了Read方法来提供从一个缓冲区读取的功能,而不是直接象其他流一样直接读取数据。就像前一个例子,任何一个组合成的装饰者,不管它有多么的复杂,还是和它的基类一样提供同样的功能。

附注:Net FrameWork中 System.IO.Stream的继承子类:

System.IO.Stream
      System.Data.OracleClient.OracleBFile
      System.Data.OracleClient.OracleLob

   System.IO.BufferedStream

   System.IO.FileStream

   System.IO.MemoryStream

   System.Net.Sockets.NetworkStream

   System.Security.Cryptography.CryptoStream

其中BufferedStream与CryptoStream就是装饰者模式中的装饰者:为其他Stream对象提供格外的功能。

posted @ 2006-03-20 13:10  Zhongkeruanjian  阅读(1858)  评论(0编辑  收藏  举报