【设计模式】原型模式

引子

  古人云:书非借不能读也。 java

  如今 IT 书籍更新快、价格贵、质量水平更是良莠不齐,实在不忍心看到用本身的血汗钱买的书不到半年就要被淘汰算法

  更不想供养使用金山快译、词霸等现代化工具的翻译们。数据库

  因而去书店办了张借书卡,这样便没有了后顾之忧了——书很差我能够换嘛!缓存

  可是,借书也有不爽的地方,就是看到有用或者比较重要的地方,不能在书旁标记下来。ide

  通常会将这页内容复印下来,这样做为本身的东西就能够对其圈圈画画,保存下来了。函数

  在软件设计中,每每也会遇到相似或者类似的问题,GOF 将这种解决方案叫做原型模式。工具

  也许原形模式会给你一些新的启迪。 编码

定义与结构

  原型模式属于对象建立模式,GOF 给它的定义为:用原型实例指定建立对象的种类,而且经过拷贝这些原型建立新的对象。spa

  在 Java 中提供了 clone()方法来实现对象的克隆,因此 Prototype 模式实现变得简单许多prototype

  使用克隆方式来建立对象与一样用来建立对象的工厂模式有什么不一样?

  前面已经提过工厂模式对新产品的适应能力比较弱:建立新的产品时,就必须修改或者增长工厂角色。

  并且为了建立产品对象要先额外的建立一个工厂对象。那经过原型模式来建立对象会是什么样子呢?

原型模式结构

  1. 客户角色:让一个原型克隆本身来获得一个新对象。
  2. 抽象原型角色:实现了本身的 clone 方法,扮演这种角色的类一般是抽象类,且它具备许多具体的子类。
  3. 具体原型角色:被复制的对象,为抽象原型角色的具体子类。

  先上一张类图   

  

  按照定义客户角色不只要负责使用对象,并且还要负责对象原型的生成和克隆。

  这样形成客户角色分工就不是很明确,因此咱们把对象原型生成和克隆功能单拿出来放到一个原型管理器中。

  原型管理器维护了已有原型的清单。客户在使用时会向原型管理器发出请求,并且能够修改原型管理器维护的清单。

  这样客户不须要编码就能够实现系统的扩展。

类图表示

   

分析

  对于抽象原型角色和具体原型角色,它们就是一个继承或者实现关系,没有什么好玩的,记住实现好 clone 方法就行了。

  那么客户是怎么来使用这些角色的对象的呢?

  最简单的方式就是:

            //先 new 一个具体原型角色做为样本
        Prototype p = new ConcretePrototype();
        ……
        //使用原型 p 克隆出一个新对象 p1
        Prototype p1 = (Prototype)p.clone();    
View Code

  固然这只是简单的表述原型模式的运行过程。

  实际运用中,客户程序与原型角色之间每每存在一个原型管理器(例子见下)。

  所以建立原型角色、拷贝原型角色就与客户程序分离开来

  这时才能真正的体会到原型模式带给咱们的效果。

//使用原型管理器后,客户得到对象的方式
Prototype p1 = PrototypeManager. getManager().getPrototype(“ConcretePrototype”);
View Code

 

  上面提到的原型管理器的实现,简单来讲就是对原型清单的维护。

  能够考虑一下几点:

  1. 要保存一个原型对象的清单,咱们可使用一个 HashMap 来实现,使原型对象和它的名字相对应;
  2. 原型管理器只须要一个就够了,因此可使用单例模式来实现控制;
  3. 实现获得、注册、删除原型对象的功能只是对 HashMap 的对应操做而已。
 1 class PrototypeManager {
 2      private static PrototypeManager pm;
 3      private Map prototypes=null;
 4      private PrototypeManager() {
 5           prototypes=new HashMap();
 6      }
 7 
 8 //使用单例模式来获得原型管理器的惟一实例
 9 public static PrototypeManager getManager() {
10     if(pm==null) {
11           pm=new PrototypeManager();
12     }
13     return pm;
14 }
15 public void register(String name , Object prototype) {
16     prototypes.put(name , prototype);
17 }
18 public void unregister(String name) {
19     prototypes.remove(name);
20 }
21 public Prototype getPrototype(String name) {
22     if(prototypes.containsKey(name)) {
23           //将清单中对应原型的复制品返回给客户
24      return (Prototype) ((Prototype)prototypes.get(name)).clone();
25 }else {
26      Prototype object=null;
27      try {
28            object =(Prototype)Class.forName(name).newInstance();
29     register(name , object);
30 } catch(Exception e) {
31     System.err.println("Class "+name+"没有定义!");
32 }
33 return object;
34 。。。
View Code

  这样当客户自定义新的产品对象时,同时向原型管理器注册一个原型对象,而使用的类只须要根据客户的须要来从原型管理器中获得一个对象就能够了。

  这样就使得功能扩展变得容易些。

  原型模式与其它建立型模式有着相同的特色:它们都将具体产品的建立过程进行包装,使得客户对建立不可知。

  就像上面例子中同样,客户程序仅仅知道一个抽象产品的接口。

  固然它还有过人之处

  1. 经过增长或者删除原型管理器中注册的对象,能够比其它建立型模式更方便的在运行时增长或者删除产品。
  2. 若是一个对象的建立老是由几种固定组件不一样方式组合而成;
  3. 若是对象之间仅仅实例属性不一样。
  4. 将不一样状况的对象缓存起来,直接克隆使用。
  5. 也许这比采用传递参数从新 new 一个对象要来的快一些。

  你也许已经发现原型模式与工厂模式有着千丝万缕的联系

  1. 原型管理器不就是一个工厂么。
  2. 固然这个工厂通过了改进(例如上例采用了 java 的反射机制),去掉了像抽象工厂模式或者工厂方法模式那样繁多的子类。
  3. 所以能够说原型模式就是在工厂模式的基础上加入了克隆方法。

  也许你要说:我实在看不出来使用 clone 方法产生对象和 new 一个对象有什么区别

  1. 原型模式使用 clone 可以动态的抽取当前对象运行时的状态而且克隆到新的对象中,新对象就能够在此基础上进行操做而不损坏原有对象; 
  2. new 只能获得一个刚初始化的对象,而在实际应用中,这每每是不够的。
  3. 特别当你的系统须要良好的扩展性时,在设计中使用原型模式也是很必要的。
  4. 好比说,你的系统可让客户自定义本身须要的类别,可是这种类别的初始化可能须要传递多于已有类别的参数,而这使得用它的类将不知道怎么来初始化它(由于已经写挂了),除非对类进行修改。
  5. 可见 clone 方法是不能使用构造函数来代替的。

  分析了这么多了,举一个使用原型模式较为经典的例子

  绩效考核软件要对今年的各类考核数据进行年度分析,而这一组数据是存放在数据库中的。

  通常咱们会将这一组数据封装在一个类中,而后将此类的一个实例做为参数传入分析算法中进行分析,获得的分析结果返回到类中相应的变量中。

  假设咱们决定对这组数据还要作另一种分析以对分析结果进行比较评定。

  这时对封装有这组数据的类进行 clone 要比再次链接数据库获得数据好的多。

  任何模式都是存在缺陷的。

  原型模式主要的缺陷就是每一个原型必须含有 clone 方法,在已有类的基础上来添加 clone 操做是比较困难的;

  并且当内部包括一些不支持 copy 或者循环引用的对象时,实现就更加困难了。

总结

  因为 clone 方法在 java 实现中有着必定的弊端和风险,因此 clone 方法是不建议使用的。

  所以不多能在 java 应用中看到原型模式的使用。

  可是原型模式仍是可以给咱们一些启迪。

 

  @成鹏致远

(blogs:lcw.cnblogs.com

(emailwwwlllll@126.com)

(qq552158509)

相关文章
相关标签/搜索