`
xiao_feng68
  • 浏览: 104906 次
  • 性别: 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软件基础教程》不仅详细...

    枇杷坞?桃花谷!-记一次有意义的春游活动作文.doc

    我们选择的目的地原以为是满是枇杷的枇杷坞,然而,当我们踏足此地时,却意外地发现,这里并非枇杷满枝,而是一个桃花盛开的桃花谷。正是这一误打误撞,让我们意外地收获了别样的美景和体验。 在枇杷坞的预期中,我...

    2020春一年级数学下册五加与减二1小兔请客课件北师大版20200321151

    课件中为此设计了多个练一练环节,让孩子在实践中不断尝试,巩固和强化他们的计算能力。在练一练1中,孩子们通过将加数与被加数结合,得到正确的和,学习到如何运用数的结构。而练一练2则通过减法运算,让孩子们学会...

    章丘一日游作文.doc

    这样的设计或许有人会感到不妥,但在我看来,它更像是一种对于李清照文化地位的象征性表达。站在山顶,我有幸一睹百脉泉全貌,那是一种历史与自然的对话,是一次视觉与心灵的双重享受。 文章的最后,我提到了在...

    辽宁省葫芦岛市六校协作体2019_2020学年高一英语上学期第一次月考试题含解析

    从这份试卷中,我们可以了解到一系列与惠灵顿相关的知识点,它们不仅丰富了我们对这个城市的文化和自然景观的认识,也为那些梦想踏足这片土地的游客们提供了宝贵的参考。 首先,试题中提到了惠灵顿的旅游景点介绍,...

    游天目湖-西游记别传作文.doc

    当唐僧师徒四人踏足于这个被青山环绕,湖水清澈如镜的仙境,他们的心中想必也是波澜不惊。天目湖的美,如同一幅精心勾勒的山水画,山色碧绿,湖水湛蓝,让人心旷神怡。作者用细腻的笔触描绘了这一切,仿佛是用一支...

    挑战程序设计竞赛(第2版)1

    无论读者是刚刚踏足程序设计竞赛领域的新手,还是早已在各大编程舞台上崭露头角的资深选手,这本书都能为你提供最直接的帮助和指导。通过学习本书中的知识和技巧,你可以更加自信地参与ACM-ICPC、Google Code Jam等...

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

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

    第一次去野生动物园作文300字.pdf

    在五一假期的第一天,我首次踏足野生动物园,这个经历让我深刻感受到动物与自然的奇妙连接,同时,它也让我意识到一个充满乐趣的野生动物园是如何成为培养孩子们对自然和动物世界兴趣的重要平台。 在众多动物中,...

    虹口-梦幻之神作文.doc

    清晨,当作者踏足于虹口,站在高高的亭子里,他眼前的是一幅宁静而生机勃勃的图画。碧绿的白沙河像一条翡翠色的丝带,河水哗哗流淌,给人一种宁静而清新的感觉。为了加强这种文学氛围,作者巧妙地引用了古诗“天门...

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

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

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

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

    电子商务管理方案书.docx

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

    给外国小朋友的一封信作文.doc

    当你踏足这片神秘的土地,你将被带回到两千多年前的秦朝,感受那场帝国的辉煌。 而当我们提起中国,我们不得不提及我们的母亲河——黄河。黄河奔流不息,滋养了中华民族的生生不息,她是中华文明的摇篮,孕育了丰富...

    河北兽鹿县_高二语文上学期第一次月考试题.doc

    河北兽鹿县的这次月考现代文阅读部分,通过精心挑选的文本内容,设计了三道选择题,这些题目旨在考验学生对原文的理解和分析能力。第一题侧重于学生对五岭历史及其作用的认识,涉及到了对历史上不同观点的辨识;第二...

    文化产业学习心得体会模板.pdf

    特别是在文化产业这一全新的学科领域里,我更是深感自己所学甚少,却也庆幸能有机会踏足这片充满生机与创新的土壤。文化产业的学习让我经历了从应试教育到素质教育的转变,从一个只会啃书本的“江湖派”学生,逐渐...

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

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

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

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

Global site tag (gtag.js) - Google Analytics