一起学习
这本书详细讲解了模式理论的由来(很多人可能不知道模式理论与中国道家思想的渊源),面向对象的软件设计原则,一直讲到二十几个设计模式,以及这些模式在Java语言中的实现等等。书中给出了大量来自华尔街系统的真实项目的例子,用来讲解设计模式在系统设计中的应用。
这本书处于理论和实践之间,是一本定位独特、风格独特的书。希望各位喜欢。
阎宏
javapatterns@yahoo.com
原始模型模式属于对象的创建模式[GOF95]。通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象。这就是原始模型模式的用意。
从孙大圣的手段谈起
---------------------
孙悟空在与黄风怪的战斗中,“使一个身外身的手段:把毫毛揪下一把,用口嚼得粉碎,望上一喷,叫声‘变!’变有百十个行者,都是一样打扮,各执一根铁棒,把那怪围在空中。” 换言之,孙悟空可以根据自己的形像,复制出很多“身外之身”来。
老孙的这种身外身的手段在面向对象的设计领域里,叫做原始模型(Prototype)模式。
Java语言的构件模型直接支持原始模型模式。所有的JavaBean都继承自java.lang.Object,而Object类提供一个clone()方法,可以将一个JavaBean对象复制一份;但是这个JavaBean必须实现一个标示接口Cloneable表明这个JavaBean支持复制。如果一个对象没有实现这个接口而调用clone()方法,Java编译器会抛出CloneNotSupportedException异常。(关于标示接口的概念及相关讨论,请见本书的“专题:Java接口”一章。)
Java的所有类都是从java.lang.Object类继承而来;而Object类提供下面的方法对对象进行复制:
protected Object clone()
子类当然也可以把这个方法置换掉,提供满足自己需要的复制方法。对象的复制有一个基本问题,就是对象通常都有对其它的对象的引用;当使用Object类的clone()方法来复制一个对象时,此对象对其它对象的引用也同时会被复制一份。
Java语言提供的Cloneable接口只起一个作用,就是在运行时期通知Java虚拟机可以安全地在这个类上使用clone()方法。通过调用这个clone()方法可以得到一个对象的复制。由于Object类本身并不实现Cloneable接口,因此如果所考虑的类没有实现Cloneable接口时,调用clone()方法会抛出CloneNotSupportedException异常。
系统可以在自己的类里面置换掉java.lang.Object提供的clone()方法,以便控制复制的过程。
克隆满足的条件
-------------
clone()方法将对象复制了一份并返还给调用者。所谓“复制”的含义与clone()方法是怎么实现的有关。一般而言,clone()方法满足以下的描述[BLOCH01]:
第一、对任何的对象x,都有:x.clone() != x;换言之,克隆对象与原对象不是同一个对象;
第二、对任何的对象x,都有:x.clone().getClass == x.getClass(),换言之,克隆对象与原对象的类型一样;
第三、如果对象x的equals()方法是定义恰当的话,那么 x.clone().equals(x)也应当是成立的。
在Java语言的API中,凡是提供了clone()方法的类,都满足上面的这些这些条件。Java语言的设计师在设计自己的clone()方法是,也应当遵守这三个条件。
一般来说,上面的三个条件中的头两个是必需的,而第三个是可选的;本书则推荐读者遵守所有的三个条件。
equals()方法的讨论
------------------
上面所给出的三个条件中,第三个条件常常被忽视。很多读者误以为可以通过继承得到java.lang.Object对象的equals()方法就足够了;而这是不对的。为了帮助读者了解equals()方法,首先看一看java.lang.Object的源代码:
public boolean equals(Object obj)
{
return (this == obj);
}
代码清单4、java.lang.Object源代码片断。
也就是说,当两个变量指向同一个对象时,equals()方法才会返还true。很显然,这并不适合于所有需要被克隆的对象。
假设被克隆的对象按照它们的内部状态是否可变划分成可变对象和不变对象的话,那么可变对象和不变对象所提供的equals()方法的工作方式应当是不同的。
可变对象只有当它们是同一个对象时equals()才会返还true,所以这样的类型可以直接从java.lang.Object继承这个方法;不变对象必须含有相同的状态才可能满足这个条件,因此不变类型必须自行实现这个equals()方法。
一个典型的例子就是Java的String。读过本书的“不变(Immutable)模式”一章的读者都应该知道,Java的String对象都是不变对象;而不变对象具有不会改变的内部状态;具有相同内部状态的String对象对于客户端就没有区别。这样一来,两个String对象的比较就应当是它们的内部状态值的比较。
本章在后面不会接触到不变对象的克隆,因此不会提供特殊的equals()方法。关于不变对象和不变模式的讨论,请读者参阅本书的“不变(Immutable)模式”一章。
原始模型模式有两种形式:第一种实现是简单形式,第二种是登记形式。这两种表现形式仅仅是原始模型模式的不同实现,但是由于它们的区别影响了模式结构的细节,因此本书把它们提前到这里讲解。
在什么情况下使用原始模型模式
假设一个系统的产品类是动态加载的,而且产品类具有一定的等级结构。
这个时候如果采取工厂模式的话,工厂类就不得不具有一个相应的等级结构;而产品类的等级结构一旦变化,工厂类的等级结构就不得不有一个相应的变化。这对于产品结构可能会有经常性变化的系统来说,采用工厂模式就有不方便之处。
在这种时候如果采取原始模型模式,给每一个产品类配备一个克隆方法(大多数的时候只需给产品类等级结构的根类配备一个克隆方法),便可以避免使用工厂模式所带来的具有固定等级结构的工厂类。
这样,一个使用了原始模型模式的系统与它的产品对象是怎么创建出来,以及这些产品对象之间的结构是怎样的,以及这个结构会不会发生变化是没有关系的。
模式的优点和缺点
抽象工厂模式有许多与原始模型模式和建造模式相同的效果,包括客户端不知道具体产品类,而只知道抽象产品类,客户端不需要知道这么多的具体产品名称。如果有新的产品类加入,客户端不需要进行改造就可直接使用。
原始模型模式又有其特有的优点:
第一、原始模型模式允许动态地增加或减少产品类。由于创建产品类实例的方法是产品类内部具有的,因此增加新产品对整个结构没有影响。
第二、原始模型模式提供简化的创建结构。工厂方法模式常常需要有一个与产品类等级结构相同的等级结构,而原始模型模式就不需要这样。对于Java设计师来说,原始模型模式又有其特有的方便之处,因为Java语言天生就将原始模型模式设计到了语言模型里面。
善于利用原始模型模式和Java语言的特点,可以事半而功倍。
第三、给一个应用软件动态加载新功能的能力。例如,一个分析Web服务器的记录文件的应用软件,针对每一种记录文件格式,都可以有一个相应的“格式类”负责。如果出现了应用软件所不支持的新的Web服务器,只需要提供一个格式类的克隆,并在客户端等登记即可;而不必给每个软件的用户提供一个全新的软件包。
第四、产品类不需要非得有任何事先确定的等级结构,因为原始模型模式适用于任何的等级结构。
原始模型模式的最主要缺点,是每一个类都必须配备一个克隆方法。配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类来说不是很难;而对于已经有的类不一定很容易。特别是当一个类引用到不支持串行化的间接对象,或者引用含有循环结构的时候。
==========================
回答某网友问:为什么不使用new关键词,而使用原始模型模式?
如果您有一个对象,有着复杂的状态。您希望得到另一个拷贝,但是同时具有一模一样的状态,如果选择从头创建一个新的对象,您将不得不重新一个一个地设置这些状态。有的时候着很麻烦,有的时候这不可能做到。这种时候,使用原始模型模式,调用一个克隆方法,就变成非常好的办法。
仍然使用孙大圣为例,如果我们从头创建一个一个的大圣对象,那么我们将不得不从石头猴子开始点化,配备上一模一样的虎皮裙,再找龙王要来一模一样的金箍棒。。。这种工作量难以想象。
孙大圣的身外身法术出神入化地解决了这个问题:大圣可以直接将本尊克隆出来,所有的化身都自动具有相同的虎皮裙,一模一样的金箍棒,一模一样的精神抖擞。这就是原始模型模式的威力。
希望您能喜欢这本书。
阎宏 博士
《Java与模式》的作者
JavaPatterns@yahoo.com
下载本文示例代码
《Java与模式》选读“原始模型(Prototype)模式”《Java与模式》选读“原始模型(Prototype)模式”《Java与模式》选读“原始模型(Prototype)模式”《Java与模式》选读“原始模型(Prototype)模式”《Java与模式》选读“原始模型(Prototype)模式”《Java与模式》选读“原始模型(Prototype)模式”《Java与模式》选读“原始模型(Prototype)模式”《Java与模式》选读“原始模型(Prototype)模式”《Java与模式》选读“原始模型(Prototype)模式”《Java与模式》选读“原始模型(Prototype)模式”《Java与模式》选读“原始模型(Prototype)模式”《Java与模式》选读“原始模型(Prototype)模式”
阅读(127) | 评论(0) | 转发(0) |