Ruby中的设计模式

作者: 我的IT技术
发布时间:2015-08-10 15:41:57

        继续 节讲述过的Singleton Proxy Iterator各模式,本节再来考察几个别的设计模式。下面按顺序来考察 Prototype Template Method Observer这三个设计模式。

4.2.2  重复使用既存对象的Prototype (原型)模式

        引用 设计模式 》一书中 解释 Prototype 模式 明确一个实例作为要生成对象的种类原型,通过 复制 该实例来生成新的对象

   《 设计模式 中这一模式的例题使用的是迷宫生成MazeFactory 类。这一例子通过拥有生成墙、房间、门等对象的原型,不需要派生就可以生成各种各样的迷宫。但是,实话说来,这个例题并没有让人真正感觉到原型的宝贵之处。

        实际上 Prototype模式本来并不太适用于 C++ 这样的静态语言。在动态语言中, Prototype 模式才能够真正发挥它的巨大威力。

通常在面向对象的语言中,首先准备好所谓的类,也就是对象的雏形,然后从类来生成实际的对象,这些做法都是理所当然的。在面向对象编程中,类是最为基本的存在。

        但是,类真的是必需的吗?Smalltalk 亚种 Self语言的设计人员就认为类并不是必 的。Self 中不存在所谓的类,基本操作并不是从类来生成对象,而是 复制

        基本思想就是如此。

        在需要新种类对象的时候,首先 复制 一个既存的对象,给 复制 的对象直接增加方法或实例变量等功能,生成最初的第一个新种类对象。如果该对象需要不止一个的话,那就从第一个原型来 复制 ,需要几个就复制几个。最初一个虽然是雏形,但它并不是所谓类这种人为生成的特别对象,它也只是一个普通的对象, 不过偶尔被用为复制的原型而已。

        雏形这个词的英语是Prototype 。像这样的不用类,而是 原型模式和复制的编程语言称为原型模式的编程语言。实际上,Prototype 模式不单是一种设计模式,也许称之为一种编程范例才更为合适。

        相对于类模式的编程,原型模式的编程的构成元素比较少,具有简单实现面向对象功能设计的倾向。因此,最近有越来越多的规格较小的编程语言采用这种模式。比如,大多数Web 浏览器中嵌入的 JavaScript 的面向对象功能就是原型模式的 最近,受到一部分 关注的Io 语言 也是原型模式的。

4.2.3  亲身体验Io 语言

        只讲理论还是难 得到具体的印象,那就让我们来边看实际程序,边来亲身体验原型模式的编程吧。虽然用Ruby 也可以进行原型模式的编程, 这次我们使用更为彻底的Io 语言( 参见 4-17 )。

4-17  使用Io 语言的原型模式的编程

        让我们来仔细看看图 4-17 吧。这是描述狗的简单例子。虽然没有实用性,但从中应该能体会到原型模式的气氛。

        (1)的部分生成新的对象,赋值给名为 Dog 的变量。请注意,这里出现的 Object Dog 都不是类。在Io 语言中, Object 只是 代表性的对象,除最基本的以外 其他 什么都不知道。以它为基础可以生成各种雏形。这里调用 clone 方法来复制一个基础对象,并给它起个名字,叫做 Dog 。刚刚复制出来的 Dog 对象跟 Object 是一样的,没有狗的任何功能。

        只是 Object 的话,什么功能都没有,是没有任何使用价值的,让我们来教给它狗的功能吧。(2) 部分中 先是给它定一个 坐下 sit 方法。把一个 method 对象赋值给 Dog 对象的 sit 属性,就给 Dog 对象追加了一个方法。

        调用 sit 方法就等于调用 " I ' m sitting/n " print ,显示 I ' m sitting. 。这一部分相当于调用字符串对象的 print 方法,从中可以体会到彻底的面向对象编程。这个例子中仅仅增加了一个 sit 方法,实际上可以根据需要想追加几个方法就可以追加几个。

        你看, Dog 对象就是这样 实现 的。再强调一遍,其中作为雏形的是狗对象,而不是类。因此, ( 3 )部分中 Dog 对象调用 sit 方法的时候,结果与其他狗一样显示出 I’m sitting.

        在像Ruby 这样类模式的语言中,作为对象雏形的类拥有与对象完全不同的方法(类的方法),而与之相对的是,原型模式的语言中根本就没有类的存在,雏形与基于它生成的对象是完全一样的。

        (4) 部分 使用 clone 方法基于雏形生成新的狗对象。这里请注意,不管是生成新的雏形,还是从雏形生成新的对象,都是仅仅使用 clone 方法实现的。在类模式的语言中,用子类化和实例化这两个不同的概念实现的程序,这里仅仅用 clone 这样一个简单的程序就实现了, 让人感动。

        当然,简单并不都是一切,原型模式有原型模式的优点,类模式也有类模式的优点,因为原型模式的语言还不太广为人知,这里特意选它作为例子, 就是为了 让大家体会一下它的单纯。

4.2.4  Ruby 中的原型

        基本上讲Ruby 是类模式的语言,但也拥有支持原型模式编程的功能 具体来说有以下3 种功能。

        (1 )复制对象的 clone 方法

        (2 )给个别对象增加方法的特异方法功能

        (3 )给个别对象增加一组功能的 extend 方法


4-18  用Ruby重写图4-17的程序


        因为Ruby 中没有像 Io 语言中 Object 这样一个对象原型,所以作为准备,程序一开始( object = Object.   new )先是生成一个 Object 类的实例。在这以后,除了语法上细微的区别之外,差不多是把Io 程序照搬过来的。

        从中我们可以看到动态语言中Prototype 模式这种令人吃惊的力量。这在必须明确对象类型的静态语言中是实现不了的。因为在静态语言中没有原型编程,是不可能给复制的对象增加新方法的。

4.2.5  编写抽象算法的Template Method (模板方法)模式

        接着来看Template Method 吧。还是引用 设计模式 中的 解释 Template Method 模式是 在父类的一个方法中定义算法的框架,其中几个步骤的具体内容则留给子类来实现。使用Template Method 模式,可以在不改变算法构造的前提下,在子类中定义算法的一些步骤

        这实际上是面向对象编程中使用继承的一般技巧。

        作为例子,让我们来看一下Ruby p 方法吧。 p 方法是程序调试中用来显示对象内容的方法。显示调试信息算法主要是:

        (1 )把对象的调试用输出信息转换成字符串

        (2 )调用 puts 方法输出

        这就是调试输出的算法,因为实在是太简单了,称之为算法 简直 有些过分。

        但实际上令人意外的是,为了输出调试信息,分别为各种对象定义调试用输出字符串是非常困难的。针对各种不同的类都要分别进行不同处理,这就很困难了,每次增加新类的时候,为支持新类也都需要做大量的工作。

        这时候使用Template Method 模式,问题就变得简单了。使用 Template Method 模式,输出调试信息的 p 方法会变成如下的代码。简单得令人意外。

def p(obj)

  puts obj.inspect

end

        这个简单的方法只是把算法的 1 和(2 )原封不动地换成了程序语言。在这个定义中,各种对象中准备调试信息的具体处理是由该对象的 inspect 方法来实现的。在定义新类的时候,只要给它定义了合适的 inspect 方法,就可以在任何时候使用 p 方法来输出适当的调试信息。

        在父类中定义抽象化的算法,调用隐 了实现细节的方法,然后在子类中实现具体的细节,这就是Template Method 模式。

4.2.6  Ruby 来尝试 Template Method

        Ruby的类库中最大限度灵活运用 Template Method 模式的部分,应该说是 Enumerable 模块和 Comparable 模块了。

        Enumerable模块中实现循环的 each 方法采用了Template Method 模式。表 4-2 Enumerable 模块的方法一览。

4-2  Enumerable提供的方法

(续)

        这些方法的定义都是仅仅依赖于 each 方法。因此,在用户定义的类中,只要定义了 each 方法,一旦把Enumerable 模块 include 进来,就都可以使用表中的32 个方法了。

        Enumerable模块实际上是用 C 编写的,用 Rudy 也可以简单地定义同样的模块,那就让我们边看 Ruby 的定义,边来考虑一下如何更好地使用 Template Method 模式

        其中一个实现起来最为简单的方法,应该是收集所有元素的 entries 方法了。 entries 方法的实现代码如图 4-19 所示。看一下图 4-19 就能明白,处理内容是很简单的。

4-19  用 Ruby 实现的 entries 方法

        (1 )生成数组。

        (2 )用 each 取出每个元素。

        (3 )把元素追加到数组里。

        (4 )最后返回数组。

        从中可以看出,这个方法只是定义了自己处理的 框架 ,对应于每个对象的处理则由 each 方法来提供。

        像Template Method 模式的这种使用方法, Comparable 模块中也是一样。

        Comparable模块利用基本的比较大小方法 <=> ,提供各种比较演算。 <=> 方法把自身与参数相比较,如果自身较大,则返回正整数 若二者相等,则返回0 若自身较小,则返回负整数。以这个方法为基础,Comparable 模块提供了 == > >= < <= 以及 between? 6 种比较运算。

        作为Comparable 提供的比较运算的代表,我们来看一看 > 方法的实现吧( 参见 4 -20 )。实际上> 方法还要加上错误处理,但基本处理如图 4 -20 所示。

4-20  用 Ruby 实现的 > 方法

        像这样,使用Template 模式,不涉及各种数据结构细节,而只在抽象的水平上编写算法的程序。也就是说,算法是在抽象水平很高的状态下表述的,同样的代码能够适用于各种各样的情况。

        像这样避免了代码的重复,从DRY 原则的观点来看 ,也是很优秀的。

4.2.7   动态语言与 Template Method 模板方法 模式

        一般Template Method 模式与继承往往是成对讨论的,但像 Enumberable 那样,只需要 include 进来,不管继承关系如何,即可以向任何类里追加功能,这一点很有魅力。本来,Ruby include 就是一种受限的多重继承, 这本 没有什么不可思议的。

        Template Method模式的这种优秀性质与语言是不是静态没有关系。像 Java 那种含有静态类型,而且不允许多重继承的语言,必须强制性地拥有继承关系。所以,像 Enumerable 这样在各种各样的类中都能利用的算法集,使用 Template Method 模式很难实现( interface 与委托的组合也不是不可能),但这不是静态语言的问题。

        但是,像Io Ruby 这种也善于原型模式编程的语言,更加进化一步,可以往特定对象里追加算法集。图 4-21 表示往特定对象里追加Enumerable 功能,虽然这个例子有点 牵强

4-21  往特定对象里追加 Enumerable 功能的示例

        尽管哪儿也没定义类,但使用 extend ,骰子对象中就能够利用Enumerable 模块的功能了。用 extend 及特异方法往特定对象里追加功能的做法,也能够用来实现 Singleton 模式。

4.2.8  避免 高度 依赖 性的观察者(Observer 模式

        观察者模式是,当某个对象的状态发生变化时,依存于该状态的全部对象都自动得到通知,而且为了让它们都得到更新,定义了对象间一对多的依存关系。

        这是控制类与类之间依存关系的一种模式。举一个例子,想想微软的Excel 软件吧。以表中的数据为基础表示图形的时候,编辑了表中的数据之后,自然希望图形的内容也跟着变化。或者,从同一组数据,想同时看到直方图和扇形图等多种图形的情况也是有的。

        能够实现这一要求的最简单的方法,应该是在表编辑功能里附加更新图形 显示 的处理。但是这样做的话,附加的是与表编辑在本质上不同的处理,使事情复杂化,更重要的是,当想要再利用表编辑功能时,还要牵连到不一定必要的图形 显示 功能。表编辑功能与图形 显示 功能之间的这种关系称为高度 依赖 性。

        一般,高度依 性不好。 本质上 ,软件是个复杂的东西,为了控制复杂性,有效 方法是将整体分割成几个相互独立的部分进行开发。但是,有了高度依 性,就不能将组成程序的 零件 (类以及子程序)进行分解,一个一个的 零件 会很大,结果就很难控制复杂性。

        观察者模式是一种 避免 这种高度依 性的手段。构成观察者模式的有两个对象,一个称为Observer (观察者),接受变更通知;另一个称为 Subject (对象)或 Observable (被观察者),发出变更通知。

        说说刚才的表编辑的例子,表数据就是Subject ,图形就是 Observer 。从观察者与被观察者这两个名字上,让人得到被动的印象, 实际处理中,被观察者会发出通知 我已经变化了哦

4.2.9  Observable 模块

        Ruby中为实现 Observer

标签: Ruby 设计模式
来源:http://www.cnblogs.com/myittec/archive/2011/06/23/2392932.ht

推荐: