`
xiao_feng68
  • 浏览: 104291 次
  • 性别: Icon_minigender_1
  • 来自: 济南
社区版块
存档分类
最新评论

踏足-设计模式(一)

阅读更多
原型模式与工厂模式的定义,本文不想在这讲太多,本文主要想在这讲一下对原型模式的一些误解--将原型模式等价于工厂模式;
    为什么会产生这种误导呢?其实也不是我们的错,关键在于设计模式这本书以及网上的其它资料很喜欢将原型和工厂方法进行比较,从而导致我们误解了原型引入的本质意义。按我的理解,原型引入的根本原因就是在于它可以利用一个原型对象(在这,我指的是实例,而非类),快速地生成一批和原型对象一样的实例。举个例子来说,你有一个类A的实例a (A a=new A()),现在你想生成一个和a一样的实例b,那么,按照原型的定义,你应该可以这样做b=a.clone()。这样,你就可以得到一个和a一模一样的实例b(即a和部b的数据成员的值完全一样)。
    上面是原型的一个简单说明,那么引入原型有什么好处呢?按我的理解,就是在于:你如果要生成一大批很相像的类的实例时,省得每次去做重复的赋值工作。再举个例子,如果你有一个类A,它有十个成员变量,现在你打算生成100个A的实例,而这些实例的变量值大部分相同(比如说七个相同),只有一小部分不一样(比如说三个),那么如果没有Prototype,那么你就得每次New一个A的对像,然后赋值,这样,你要重复100次同样的七个变量的赋值工作,显然,这样很麻烦。现在你有了原型,那么问题就简单了,你只要生成一个A的实例,再通过clone来生成其它的实例,然后再一一修改其它实例不同的地方。
    可能我这么讲,大家不信,那下面,再让我们来看看Java中活生生的原型应用。
学过Java的人都知道,在Java中,有一个clone()函数,这个函数的功能,就是返回一个和当前调用它的对象一样的实例。那么Java中为什么要引入这个函数呢?在<<Think in Java>>一书中,作者如是解释:
    如果,你要将一个对象的引用作为参数传进去,但又不希望函数改变对象的值,那么,你该怎么办?由于在Java中对对象没有像C++那样的Const修饰符,所以,为了实现这个功能,Java中引入了clone函数,使得你将对象的引用作为参数传进函数时,这个函数可以调用该对象的Clone方法生成该对象的一份拷贝,从而达到不修改原对象的目的。
    我之所以用上面这么多篇幅来讲述原型本质,目的就在于希望各位不要像我一样,把原型的功能与它的意义给混了,以致于当真正要使用原型来解决问题时,却不知可以使用它。
    好了,上面说了原型的本质意义--至少我认为是这样的。那为什么很多资料喜欢将原型同工厂模式进行比较呢?不知是不是巧合,虽然原型引入的初衷是像我上面所说,但他实现起来,却又完全可以达到工厂模式的郊果(后面,我会用代码实现可以用工厂模式实现的Mac,Win产品系列生成问题)。而且,用起来甚至比工厂模式更方便、灵活。对于工厂模式与原形模式在功能上的这点巧合,我想也许是因为本来工厂模式和原型模式都是创建型模式,这样,他们的基本功能都能生成对象,因而使得原型模式在功能上可以代替工厂模式。对这两种模式在功能上的相同点,程序员2001年第11期杂志上有一篇”非鱼“写的文章,作者理解得非常巧妙,即:如果你将工厂模式的UMl图对折,你得到的就是Prototype原型的UML图。有兴趣比较这两种模式的朋友,可以去参考这篇文章。
    接下来,让我们在实现机制上来看看原型模式为什么可以实现工厂模式的功能(本文只限于Java语言)。在Java中,对于原型的实现,其实根本不用我们做,在object类中早就定义了一个clone函数,而这个函数,就使得我们可以动态地生成对象的当前拷贝。即然这样,那么让我们来看看,如果要实现工厂模式的功能,我
们该如何使用原型模式为做到呢?
    工厂模式实现的生产产品的功能,关键是利用了继承的特性。也就是说,你生成的产品,一定是由同一个抽象产品类派生出来的。所以,在工厂模式下,你如果要生成一类产品,就要引入一个抽像产品类,然后再由它派生出具体产品。同样,在原型模式中,你完全可以同样定义一个这样的“抽象产品--具体产品”层次,再利用具体产品本身的clone功能来产生具体产品本身。从而达到实现工厂模式功能的目的。可能说到这,大家有点糊涂了。实际上,在原型模式中,每个具体产品就扮演了工厂模式里的具体工厂的角色(为什么会这样,其实很简单,因为,每个具体产品都具有生成
自己拷贝的功能?从这种意义上讲,难道这不正是工厂的作用吗?)。另外,要在Java中利用原形模式实现工厂模式的功能,则更为简单,因为object已经为我们实现了clone函数,且对于clone方法,Java中默认是:如果A是父类且A实现了clone函数,B是A的子类,则B不用实现clone函数,它只要调用父类的clone函数,Java就会在运行时动态地为我们生成正确的B的对象。理解这点的关键在于,所有类实现的 clone操作都是调用object的clone方法。这也就是说,我上面所说的父类A根本就不用自己实现clone方法,而仅仅是调用父类(object)的clone方法而已。好,到了这,读者也许又有疑问了,既然所有的cloen操作都是由object实现的,而在java中所有的自定义类默认都是由object派生而来,那这样的话,应该所有的类都自动就具有了clone自己的能力?
    确实,如果object不将它的clone函数声明为protect的话,情况的确如此。但Java为了安全方面的原因,所以没有将clone方法公开,而是声明为保护类型,这样的话,子类是不可以直接调用object类的clone方法的,而必须做到如下两点:
       1.必须实现Cloneable接口;
       2.必须声明一个clone方法,来调用object的clone函数;
       Java在调用父类的clone函数时,都会在运行时动态地进行检查,如果发现调用的类不符合上面的任何一点,则会抛出一个异常。
    明白了上面的原因,那么如果我们希望某个类具备clone自身的能力,那么,我们可以这样做:
       1.直接按上面所说,自己实现clone操作;
       2.声明一个抽象父类,实现上面的clone操作并将它声明为公开方法,再由此类派生出子类,这样,所有的  子类只要调用父类的clone方法,就能够正确地拷贝自己。
    通常,我们都是使用第一种方式,但在我们现在讨论的如何用原型模式实现工厂模式的功能的问题中,我们最好是采用第二种方式。
    最后,让我们通过具体的代码来看看如何用Prototype模式实现工厂模式的功能。

问题:
      现有两类产品 1-Ram,2--Cpu,现在要生成具体的产品
         MacRam,MacCpu和WinRam,WinCpu.

代码如下:
/**
*A:Abstract
*C:Concrete
*/

/** 定义抽象产品Ram的类 APrototypeRam
  * 同时他也是抽象工厂
  */

abstract class APrototypeRam implements Cloneable {
public Object clone() {
  Object o=null;
  try {
   o=super.clone();//调用父类,即Object的clone()
  }
  catch(CloneNotSupportedException e) {
     System.err.println("APrototypeRam is not cloneable!");
  }
  return o;
}
}

/** 定义抽象产品Ram的类APrototypeProductCpu
  * 同时他也是抽象工厂
  */

abstract class APrototypeCpu implements Cloneable {
public Object  clone() {
  Object o=null;
  try {
   o=super.clone();//调用父类,即Object的clone()
  }
  catch(CloneNotSupportedException e) {
    System.err.println("APrototypeCpu is not cloneable!");
  }
  return o;
}
}

/** 定义具体产品MacRam的类CPrototypeMacRam
  * 同时他也是具体工厂
  */

class CPrototypeMacRam extends APrototypeRam{
public String toString() {
  return "MacRam";
}
}

/** 定义具体产品WinRam的类CPrototypeWinRam
  * 同时他也是具体工厂
  */

class CPrototypeWinRam extends APrototypeRam {
public String toString() {
  return "WinRam";
}
}

/** 定义具体产品MacCpu的类CPrototypeMacCpu
  * 同时他也是具体工厂
  */

class CPrototypeMacCpu extends APrototypeCpu{
public String toString() {
  return "MacCpu";
}
}

/** 定义具体产品WinCpu的类CPrototypeWinCpu
  * 同时他也是具体工厂
  */

class CPrototypeWinCpu extends APrototypeCpu{
public String toString() {
  return "WinCpu";
}
}

/** 客户端,使用CPrototypeRam和CPrototypeCpu生成如下产品
  * MacRam,MacCpu,WinRam,WinCpu
  */

public class Prototype {
public static void main(String[] args) {
  /**
    * 在生成产品之前,先生成原型产品,以便后面利用它们成批生产相同产品
    * 其作用等价于产品工厂
    */
  CPrototypeMacRam prototypeMacRam=new CPrototypeMacRam();
  CPrototypeWinRam prototypeWinRam=new CPrototypeWinRam();
  CPrototypeMacCpu prototypeMacCpu=new CPrototypeMacCpu();
  CPrototypeWinCpu prototypeWinCpu=new CPrototypeWinCpu();
    
  CPrototypeMacRam MacRam=(CPrototypeMacRam)prototypeMacRam.clone();
  CPrototypeWinRam WinRam=(CPrototypeWinRam)prototypeWinRam.clone();
  CPrototypeMacCpu MacCpu=(CPrototypeMacCpu)prototypeMacCpu.clone();
  CPrototypeWinCpu WinCpu=(CPrototypeWinCpu)prototypeWinCpu.clone();
  System.out.println("打印原形产品与它的克隆产品与比较异同!");
  System.out.println("prototypeMacRam:"+prototypeMacRam+"  Cloned:"+MacRam);
  System.out.println("prototypeWinRam:"+prototypeWinRam+"  Cloned:"+WinRam);
  System.out.println("prototypeMacCpu:"+prototypeMacCpu+"  Cloned:"+MacCpu);
  System.out.println("prototypeWinCpu:"+prototypeWinCpu+"  Cloned:"+WinCpu);

}
}

    通过上面代码,我们可以清楚地看到,用Prototype模式实现工厂模式更为简单,如果再配上原型管理器的话,那么Prototype模式则会变得更为灵活,限于篇幅,本文没有讲到原型管理器,有兴趣的朋友可以参看后文列出的参考文献。但同时,我们也发现,使用原形模式时,有一个不足之处,即在客户端代码里,我们必须显示进行类型转换,这样可能导致错误。为了改正这一点,我想,我们可以使用真正的工厂模式将Prototype模式再封装一遍。对工厂模式的这项功能,恐怕,Prototype原形模式就无能为力了。
     总之,工厂模式和原形模式虽然在引入目的上不同,但在实现上,原形模式可以实现工厂模式同样的功能。但读者也不要因为这样,而将两者混为一体,因为,反过来,在将原形模式作为生成本身拷贝的这项功能使用时,工厂模式根本无法取代它。

     但实际上,Prototype所模仿的工厂模式的功能,只是“形似而神不似”。我们并不能真正地用原型来代替Factory的功能。
      为什么这么说呢?首先让我们来看一看Factory模式引入的本意:Factory引入的目的是将创建类对象的职责从一处转移到另一处。举个例子来说,如果你不希望客户端具有创建某个类对象的能力,那么,你就可以利用工厂模式将创建类对象的职责从客户端转移到服务器端(或者说系统端)。这个例子的最成功的运用就是在Com中,客户只能通过工厂方法创建类的实例,而不充许客户直接创建类的实例。
      好了,明白了这一点,让我们再来看看Prototype模式所实现的工厂模式的功能到底是怎么一回事!的确,Prototype模式确实有创建类对象的能力,但是,这种能力,并不符合工厂模式运用的目的,即将创建类对象的职责转移。因为,我们知道,如果要利用Prototype来创建产品,那么,在创建具体的产品之前,我们必须要先生成原型产品。但实际上,原型产品已是符合我们要求的产品,从这种意义上来讲,再通过原型产品来克隆出一个一样的产品根本就没有任何价值。再者,既然原型产品必须暴露给客户,这也违背的工厂模式的原则:即责任分离。
      综上所述,如果我们从Prototype模式和Factory模式运用的目的上来看待二者的话,那么这两种模式完全不是同一回事。虽然,Prototype模式看似可以实现Factory模式的创建类对象的功能,但这种相似能力只是“形似而神不似”。
      因此,今后我们在运用这两种模式时,一定要分清楚我们的真正目的。这样,才可以更好地选对模式,以方便我们的设计。
分享到:
评论

相关推荐

    c++源码 走迷宫问题 算法设计

    有一个m*n格的迷宫(表示有m行、n列),其中有可走的也有不可走的,如果用1表示可以走,0表示不可以走,文件读入这m*n个数据和起始点、结束点(起始点和结束点都是用两个数据来描述的,分别表示这个点的行号和列号)。...

    Apollo-11-master.zip

    在人类历史上,阿波罗11号(Apollo 11)登月任务无疑是科技与勇气的一次伟大结合,它标志着人类首次踏足月球。这次壮举的背后,隐藏着一系列精密的计算和技术,其中关键的部分就是阿波罗导航计算机(Apollo Guidance...

    电子书 TopSolid软件基础教程.pdf

    - **踏足板设计**:结合装配动画和AVI视频制作,增强交互体验。 - **折叠椅设计**:探索更加灵活多变的设计方案。 - **创建刀具**:自定义刀具,适应特定加工需求。 综上所述,《TopSolid软件基础教程》不仅详细...

    (源码)基于Qt框架的足迹记录系统.zip

    本项目是一个基于Qt框架开发的足迹记录系统,仿照“西瓜足迹”应用的功能设计。用户可以通过该系统记录和查看自己踏足的城市信息,包括城市的名称、踏足比例等。系统提供了友好的图形用户界面,支持用户登录、城市...

    防盗报警优秀设计—核桃式防范方案

    设计使用20对双光束围墙用红外对射探测器进行防范,貌似严密,其实这种双光束对射探测器光束防范高度仅为20cm,极易被跨越或钻、爬等方式闪避,也正是这个致命的防范弱点,被2名专业盗贼趁屋内无人,由正门墙边的...

    关于美的的电子商务方案书

    经过分析其他同类型网站,我们除了保留一些传统企业网上电子商务的基本功能外,更为美的海外网站设计一系列独特功能: 9  海外企业网站特徵 10 五、海外网站后台支持 13 六、项目管理 14  项目时间表 14  人力...

    电子商务管理方案书.docx

    电子商务管理方案书的主要目的是为了帮助美的海外市场部建立一个有知名度、内容丰富、功能齐备的网站,以踏足国际电子商务的市场。 在概述中,我们了解到美的网站的定位和优势。美的网站的定位是为了提供新颖有效的...

    纪念碑谷MonumentValley-少儿编程scratch项目源代码文件案例素材.zip

    《纪念碑谷Monument Valley》是一款深受玩家喜爱的解谜类游戏,以其独特的艺术风格和精巧的关卡设计著称。在这个少儿编程的Scratch项目...这是一个极好的起点,让孩子们踏足编程世界,为未来的技术学习打下坚实的基础。

    美的海外网站组建及电子商务方案书.doc

    从网站功能分析,美的集团需要将现有网站转化成一个有知名度、内容丰富、功能齐备的网站,以踏足国际电子商务的市场。 四、海外网站前台构建 美的海外网站的前台构建需要经过深入的分析和设计。我们需要对其他同...

    七年级上语文第19课《月亮上的足迹》同步练习2精选.doc

    - 美国宇航员阿姆斯特朗、科林斯、奥尔德林终于代表全人类**踏足**了月球,**迈出了**人类探索太空的重要一步,**树立了**宇航事业的一块新的里程碑。 4. 正确选项: - A项正确,阿姆斯特朗是第一个踏上月面的人...

    【宋词赏析,许棐,后庭花一春不识西湖面】一春不识西湖面.docx

    “一春不识西湖面”开篇便设定了情境,女主角因为思念之情,整个春季未曾踏足西湖,这不仅暗示了时间的漫长,也突出了她的内心世界对外界美景的疏离。"翠羞红倦"形象地描绘了女子因思念而面容憔悴,失去了与春色相...

    reeyoo+openfire

    Openfire是一款基于Jabber协议的开源服务器软件,支持多种操作系统,并且自带Java环境。如意800(rooyee)则是一个可能的客户端应用,用于与Openfire服务器通信。 ### Openfire的安装和配置 1. **安装环境**: - ...

    华为-五力模型.pdf

    具有很强的价格谈判能力,这种能力对企业的盈利水柴油机行业进入壁垒和机会分析进入壁垒:1、资金:研发、生产柴油机需要大量的资金,没有足够的启动资金,就不能踏足这个行业。2、技术:想要开发生产柴油机需要有...

    一航天技术发展概况.pptx

    1969年,"阿波罗11号"飞船搭乘"土星5号"火箭成功登月,人类首次踏足月球。 航天技术的应用广泛,全球已发射了4000多个航天器,其中90%是应用卫星。这些卫星服务于各种用途,如通信、气象预报、导航、军事侦察、地球...

    暑假见闻-美丽的青岛作文.doc

    在暑假里,我有幸踏足这片神奇的土地,亲身体验了青岛的美丽。大海是青岛的一大亮点,那无垠的海面如同一面巨大的镜子,在阳光的照耀下闪烁着迷人的光芒。海浪在不同状态下展现出不同的风貌,有时平静如镜,有时翻涌...

    cocos2d中文版

    cocos2d for iPhone是一款专为苹果设备设计的游戏开发框架,适用于iPhone、iPod Touch以及iPad平台。它采用Objective-C语言编写,这使得开发者能够充分利用苹果的官方文档和SDK,实现更深层次的集成与优化。cocos2d...

    《横渠四句》阅读答案-横渠四句.docx

    然而,当人类踏足这些地方时,通过科学研究和技术手段,我们能够揭示出这些星球的真实面貌,从而让它们不再是一片未知的黑暗之地。因此,“为天地立心”实际上意味着人类通过探索、研究和理解自然,赋予了自然界以...

    屈原、昭君故里游导游词.doc

    屈原和昭君,两位在中国历史文化中举足轻重的人物,他们的故事和影响力深深地烙印在了中国的文化基因中。屈原,战国时期的楚国诗人,他...每一次踏足这片土地,都仿佛能听到历史的回声,感受到那份跨越千年的文化传承。

    太阳系大家族3.ppt

    1969年,人类首次踏足月球,阿波罗11号飞船的阿姆斯特朗和阿尔德林实现了这一壮举。阿姆斯特朗那句著名的台词:“对一个人来说,这是一小步;对于人类来说,这是巨大的一步。”寓意深远。它意味着人类科技的飞跃,...

Global site tag (gtag.js) - Google Analytics