Ruby中的设计模式——《松本行弘的程序世界》

    《 设计模式 一书是用C++ Smalltalk 介绍模式实例的。看了那些例子,大家都会感觉到,绝大多数的模式用 Smalltalk 实现起来非常简单。这是为什么呢?

         因为Smalltalk 没有静态类型,所以也就不需要匹配类型的模板等机制,也不需要仅仅为满足类型要求而进行继承,这就是 Smalltalk 简单的理由。而且,由于语言本身的动态性质,有些模式根本不必要以模式的形式来抽象出来就可以得到简洁的实现,这也是 Smalltalk 显得简单的原因之一。

         Ruby在很多方面很像 Smalltalk ,实现设计模式也毫无困难。一般说来,用 Ruby 来实现设计模式的场合,要比 C++ 简洁得多,有些模式用现成的库就足以表现。

         下面以Ruby 为中心,让我们来看几个在实际设计中 用设计模式的例子。


Singleton( 单件 模式

 

    首先, 让我们来看一下最简单的设计模式之一,Singleton 模式。 Singleton 模式用来保证某个类的实例只有一个。

         为什么需要Singleton 模式呢?比如作为其他对象的雏形而存在的对象(用于 Prototype 模式),系统全体只存在唯一一个的对象等,都要用到 Singleton 模式。

         用Ruby 实现 Singleton 模式的方法有几个 让我们按顺序来逐一说明。

使用singleton 库的方法

         Ruby已经以库的形式实现了 Singleton 模式。如图 4-1 所示,使用singleton 库的话,在任意的类里只要包含( include )上 Singleton 模块,那个类就变成了 Singleton 模式的对象。

         要想取得Singleton 模式的类的对象,像图 4-1 最后一行那样,使用该类的 instance 方法。如果该类对象还没有生成, instance 方法会生成该类对象并返回。如果该类对象已经生成, instance 方法就返回既有对象。

使用类或模块

         C++ Java 是不能把类作为对象来使用的,与之不同的是, Smalltalk Ruby 能把类也作为对象来处理。因此,在类或模块里定义一个方法就可以实现 Singleton 模式( 参见 4- 2)。

4-1  使用 singleton 库的 Ruby 代码

4-2  利用类定义来实现 Singleton 模式的代码

把一般的对象作为Singleton 来使用

        为了把一个类的对象限制成只有一个,并不一定需要对对象的一般生成方法加以限制。我们可以生成一个一般的对象,然后遵守绅士协定,不要再生成 其他更 多个对象,也就 了( 参见 4- 3)。

使用对象和特异方法

         其实还有不用类就可以实现的方法。Ruby 可以在对象生成之后再增加新的方法,这样我们就可以生成一个 Object 类的对象,然后给它追加必要的功能( 参见 4- 4)。

4-3  在编程上下点功夫来实现 Singleton 模式

4-4  利用特殊方法来实现 Singleton 模式的代码

         这种使用特异方法的办法是很符合Ruby 特征的。 Ruby 自身的 main (最高层的 self ARGF 虚拟文件,用来代表参数所指定的文件 等也都是用这种方法实现的。


Proxy( 代理 模式

          Proxy模式是为某个对象提供代理对象的模式。为什么需要 Proxy 模式呢?

         假设有个生成代价非常大的对象。 如果 在还不知道是否真正需要该对象的时候就事先生成它的话,可能会带来很大的浪费。但话虽这么说,不生成对象的话什么事也做不了。这时候代理对象就有用武之地了。

         比如字处理软件,它利用Proxy 对象来处理嵌入图像,把嵌入图像的生成处理延迟到需要表示的瞬间才来进行。

         Ruby的库中也有使用 Proxy 模式的。比如 tempfile 库,它不用指定文件名就可以生成临时的工作文件( 参见 4- 5)。

         Tempfile类与实际负责文件输出的 IO 类没有继承关系,它的有关输入、输出处理的方法都通过 Proxy 转送到实际的 IO 类对象。因此,通过使用 Tempfile 类的对象,在任何有必要的时候也都可以使用相关的 IO 对象。

         Proxy模式也可以用 Ruby 的库来实现。使用 delegate 库就可以了。 delegate 是委托的意思。 Tempfile 类也是用 delegate 库来实现的( 参见 4- 6)。

4-5  采用 Proxy 模式的 tempfile 库的使用示例

4-6  使用 delegate 库来实现 Proxy 模式的例子

 

         看一下就知道,delegate 库的源代码是相当复杂的,但基本上只是把被调用的方法都转送到本来的对象那里去。这里使用的是 Ruby method_   missing 方法。

          Ruby中对对象 A 调用它所不知道的方法的时候, A method_missing 方法就会被调用。传递给 method_mis sing 的参数是在原来调 用方法的参数之前加上不存在的方法名。利用这一框架就可以很简单地实现Proxy 模式( 参见 4-7 )。

         怎么样,真的是非常简单吧。但是,这种实现方式也有不灵光的时候。Proxy 类固有的方法被调用的时候,是不会转送到 method_missing 方法的。也就是说,Proxy 类的父类 Object 类的方法是转送不了的。

4-7  使用 method_missing 方法来实现Proxy 模式的例子

         如果这样的情况也要对应的话,就会稍微麻烦一些。实际上,delegate 库除了空行和注释以外,长达 114 行。比图 4-7 要复杂得多。

         delegate库使用起来虽然很简单,但方法转送的对象仅限于既有的对象。因此,在最开始举的字处理软件例子中,要想达到延迟图像生成的目的,直接使用 delegate 是不行的。我们可以像图 4-8 那样,从Delegator 派生一个子类 ImageProxy 来达到这一目的。

    __getobj__ 方法是Delegator 对象取得方法转送对象的方法。通过 重写 这个方法,ImageProxy 会在实际访问图像对象的时候才来生成图像对象。 C++ 会用 operator-> 或者 operator* 来代替 __getobj__

4-8   Proxy 模式延迟对象生成的例子


Iterator( 迭代器 模式

         Iterator模式提供按顺序访问包含有多个对象的集合对象中各元素的方法。即使不知道对象的内部构造,也可以按顺序访问其中的每个元素。 Iterator 模式是为集合对象另外准备用来控制循环处理的对象,就像 C++ Java 一样。我们称这个循环控制对象为 Iterator 。也称为光标。

         图 4-9 Iterator 模式的类构成图。调用集合对象(图 4-9 Iteratable )的 Create Iterator() 方法,就会返回自己对应的Iterator 对象。 Iterator 对象会记住现在所指向的 Iteratable 元素,调用 Next() 方法可以返回集合的下一个元素。要想知道集合中是否还有别的元素,可以调用 IsDone() 方法来确认。图 4-10 是利用Iterator 模式的程序示例。 Iterator 模式实现的是所谓外部迭代器的循环控制抽象化。

4-9   J ava Iterator 模式的类构成图

4-10   Java 版外部迭代器的用法

         而Ruby 是用块来对集合的各元素进行循环处理的。作为设计模式,使用块进行循环的抽象化属于 Visitor (访问者)模式。但因为语言本身就支持这样的循环,所以也就不需要 Iterator 这样的对象了。这实在是太基本的东西了,也许都不应该称之为设计模式了。

posted @ 2011-06-17 15:17  我的IT技术  阅读(252)  评论(0编辑  收藏  举报