长平狐 Java程序员从笨鸟到菜鸟之(三十四)大话设计模式(五)建立者模式和原型模式

建立者模式是建立型模式中最负责的一个设计模式了,建立者负责构建一个对象的各个部分,而且完成组装的过程.构建模式主要用来针对复杂产品生产,分离部件构建细节,以达到良好的伸缩性。 把构造对象实例的逻辑移到了类的外部,在这个类外部定义了这个类的构造逻辑。它把一个复杂对象的构过程从对象的表示中分离出来。其直接效果是将一个复杂的对象简化为一个比较简单的对象。它强调的是产品的构造过程。

        在软件系统中,有时候面临着“一个复杂对象”的建立工做,其一般由各个部分的子对象用必定的算法构成;因为需求的变化,这个复杂对象的各个部分常常面临 着剧烈的变化,可是将它们组合在一块儿的算法确相对稳定。如何应对这种变化?如何提供一种“封装机制”来隔离出“复杂对象的各个部分”的变化,从而保持系统 中的“稳定构建算法”不随着需求改变而改变?这就是要说的建造者模式。 java

意图:将一个复杂对象的构建与其表示相分离,使得一样的构建过程能够建立不一样的表示。 算法

类图: 设计模式

   

  抽象建造者(Builder):给出一个抽象接口,以规范产品对象的各个组成成分的建造。这个接口规定要实现复杂对象的哪些部分的建立,并不涉及具体的对象部件的建立。
    具体建造者(Concrete Builder):实现Builder接口,针对不一样的商业逻辑,具体化复杂对象的各部分的建立。 在建造过程完成后,提供产品的实例。
    指导者(Director):调用具体建造者来建立复杂对象的各个部分,在指导者中不涉及具体产品的信息,只负责保证对象各部分完整建立或按某种顺序建立。
    产品(Product):要建立的复杂对象。
ide

适用范围: ui

1.须要生成的产品对象有复杂的内部结构。 this

2.须要生成的产品对象的属性相互依赖,建造者模式能够强迫生成顺序。 spa

3.在对象建立过程当中会使用到系统中的一些其余对象,这些对象在产品对象的建立过程当中不易获得。 .net

效果: prototype

1.建造者模式的使用时的产品的内部表象能够独立的变化。使用建造者模式可使客户端没必要知道产品内部组成的细节。 设计

2.每个Builder都相对独立,而与其余的Builder无关。

3.模式所建造的最终产品易于控制。

下面咱们来看一下经典建立者模式的一个实现形式,而后针对这个经典模式后面提出几个改进方案,咱们这里以上面讲述的服装的过程做为例子来讲明下建立者模式的原理和思想,但愿你们也能灵活的运用到实际的项目中去。达到学以至用的目的。

咱们来看看具体的代码实现:

  1. package builder;  
  2.   
  3. public class Product {  
  4.   
  5. ArrayList<String> parts = new ArrayList<String>();  
  6.   
  7. public void add(String part)  
  8.   
  9. {  
  10.   
  11. parts.add(part);  
  12.   
  13. }  
  14.   
  15. public void show()  
  16.   
  17. {  
  18.   
  19. System.out.println("产品建立------------");  
  20.   
  21. for(String part : parts)  
  22.   
  23. {  
  24.   
  25. System.out.println(part);  
  26.   
  27. }  
  28.   
  29. }  
  30.   
  31. }  
  32.   
  33. public abstract class Builder {  
  34.   
  35. public abstract void BuildPartA();  
  36.   
  37. public abstract void BuildPartB();  
  38.   
  39. public abstract Product getResult();  
  40.   
  41. }  
  42.   
  43. public class ConcreteBuilder1 extends Builder{  
  44.   
  45. private Product product = new Product();  
  46.   
  47. @Override  
  48.   
  49. public void BuildPartA() {  
  50.   
  51. product.add("部件A");  
  52.   
  53. }  
  54.   
  55. @Override  
  56.   
  57. public void BuildPartB() {  
  58.   
  59. product.add("部件B");  
  60.   
  61. }  
  62.   
  63. @Override  
  64.   
  65. public Product getResult() {  
  66.   
  67. return product;  
  68.   
  69. }  
  70.   
  71. }  
  72.   
  73. public class ConcreteBuilder2 extends Builder{  
  74.   
  75. private  Product product = new Product();  
  76.   
  77. @Override  
  78.   
  79. public void BuildPartA() {  
  80.   
  81. product.add("部件x");  
  82.   
  83. }  
  84.   
  85. @Override  
  86.   
  87. public void BuildPartB() {  
  88.   
  89. product.add("部件y");  
  90.   
  91. }  
  92.   
  93. @Override  
  94.   
  95. public Product getResult() {  
  96.   
  97. return product;  
  98.   
  99. }  
  100.   
  101. }  
  102.   
  103. public class Director {  
  104.   
  105. public void Construct(Builder builder)  
  106.   
  107. {  
  108.   
  109. builder.BuildPartA();  
  110.   
  111. builder.BuildPartB();  
  112.   
  113. }  
  114.   
  115. }  
  116.   
  117. public class TestBuilder {  
  118.   
  119. public static void main(String[] args) {  
  120.   
  121. Director director = new Director();  
  122.   
  123. Builder b1 = new ConcreteBuilder1();  
  124.   
  125. Builder b2 = new ConcreteBuilder2();  
  126.   
  127. director.Construct(b1);  
  128.   
  129. Product p1 = b1.getResult();  
  130.   
  131. p1.show();  
  132.   
  133. director.Construct(b2);  
  134.   
  135. Product p2 = b2.getResult();  
  136.   
  137. p2.show();  
  138.   
  139. }  
  140.   
  141. }      


经过上面的代码,咱们给出了经典建立者模式的核心代码形式,那么针对上面无疑有如下的几个缺点:

1Ibuilder接口必须定义完整的组装流程,一旦定义就不能随意的动态修改。

2Builder与具体的对象之间有必定的依赖关系,固然这里能够经过接口来解耦来实现灵活性。

3Builder必须知道具体的流程。

那么针对上面的几个问题,咱们如何来解决呢?我想前面的建立型模式已经给我了足够的经验,仍是经过配置文件或者其余的形式来提供灵活性。

原型模式

        用原型实例指定建立对象的种类,而且经过拷贝这些原型建立新的对象。 Prototype原型模式是一种建立型设计模式,Prototype模式容许一个对象再建立另一个可定制的对象,根本无需知道任何如何建立的细节,工做原理是:经过将一个原型对象传给那个要发动建立的对象,这个要发动建立的对象经过请求原型对象拷贝它们本身来实施建立。它主要面对的问题是:某些结构复杂的对象的建立工做;因为需求的变化,这些对象常常面临着剧烈的变化,可是他们却拥有比较稳定一致的接口。 

原型模式最大的特色是克隆一个现有的对象,这个克隆的结果有2种,一种是是浅复制,另外一种是深复制,这里咱们也会探讨下深复制和浅复制的原理,这样可能更方便你们理解这个原型模式的使用。咱们都知道,建立型模式通常是用来建立一个新的对象,而后咱们使用这个对象完成一些对象的操做,咱们经过原型模式能够快速的建立一个对象而不须要提供专门的new()操做就能够快速完成对象的建立,这无疑是一种很是有效的方式,快速的建立一个新的对象。

原型模式的原理图:

原型模式的主要思想是基于现有的对象克隆一个新的对象出来,通常是有对象的内部提供克隆的方法,经过该方法返回一个对象的副本,这种建立对象的方式,相比咱们以前说的几类建立型模式仍是有区别的,以前的讲述的工厂模式与抽象工厂都是经过工厂封装具体的new操做的过程,返回一个新的对象,有的时候咱们经过这样的建立工厂建立对象不值得,特别是如下的几个场景的时候,可能使用原型模式更简单也效率更高。

主要运用场合:

1、若是说咱们的对象类型不是刚开始就能肯定,而是这个类型是在运行期肯定的话,那么咱们经过这个类型的对象克隆出一个新的类型更容易。

二、有 的时候咱们可能在实际的项目中须要一个对象在某个状态下的副本,这个前提很重要,这点怎么理解呢,例若有的时候咱们须要对比一个对象通过处理后的状态和处 理前的状态是否发生过改变,可能咱们就须要在执行某段处理以前,克隆这个对象此时状态的副本,而后等执行后的状态进行相应的对比,这样的应用在项目中也是 常常会出现的。

三、当咱们在处理一些对象比较简单,而且对象之间的区别很小,可能只是很固定的几个属性不一样的时候,可能咱们使用原型模式更合适

深复制和浅复制:

浅复制(浅克隆)
被复制对象的全部变量都含有与原来的对象相同的值,而全部的对其余对象的引用仍然指向原来的对象。换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象。
深复制(深克隆)
被复制对象的全部变量都含有与原来的对象相同的值,除去那些引用其余对象的变量。那些引用其余对象的变量将指向被复制过的新对象,而再也不是原有的那些被引用的对象。换言之,深复制把要复制的对象所引用的对象都复制了一遍。

下面举一个实例来实现如下原型模式:

  1. import java.io.ByteArrayInputStream;  
  2. import java.io.ByteArrayOutputStream;  
  3. import java.io.IOException;  
  4. import java.io.ObjectInputStream;  
  5. import java.io.ObjectOutputStream;  
  6. import java.io.Serializable;   
  7.   
  8. class Prototype implements Cloneable,Serializable{  
  9.    
  10.  private String str;  
  11.  private Temp temp;  
  12.    
  13.  public Object clone()throws CloneNotSupportedException{ //浅克隆  
  14.   Prototype prototype=(Prototype)super.clone();  
  15.   return prototype;  
  16.  }  
  17.  public Object deepClone()throws IOException,ClassNotFoundException{ //深克隆  
  18.   ByteArrayOutputStream bo=new ByteArrayOutputStream();  
  19.   ObjectOutputStream oo=new ObjectOutputStream(bo);  
  20.   oo.writeObject(this);  
  21.     
  22.   ByteArrayInputStream bi=new ByteArrayInputStream(bo.toByteArray());  
  23.   ObjectInputStream oi=new ObjectInputStream(bi);  
  24.   return oi.readObject();  
  25.  }  
  26.  public String getStr() {  
  27.   return str;  
  28.  }  
  29.  public void setStr(String str) {  
  30.   this.str = str;  
  31.  }  
  32.  public Temp getTemp() {  
  33.   return temp;  
  34.  }  
  35.  public void setTemp(Temp temp) {  
  36.   this.temp = temp;  
  37.  }  
  38.    
  39. }  
  40. class Temp implements Serializable{  
  41.    
  42. }  
  43. public class Test {  
  44.   
  45.  public static void main(String[] args)throws CloneNotSupportedException,ClassNotFoundException ,IOException{  
  46.     
  47.   Prototype pt=new Prototype();  
  48.   Temp temp=new Temp();  
  49.   pt.setTemp(temp);  
  50.   pt.setStr("Hello World");  
  51.   System.out.println("使用浅克隆方法进行建立对象");  
  52.   Prototype pt1=(Prototype)pt.clone();  
  53.   System.out.println("=============================");  
  54.   System.out.println("比较pt和pt1的str的值:");  
  55.   System.out.println(pt.getStr());  
  56.   System.out.println(pt1.getStr());  
  57.     
  58.   System.out.println("修改pt1对象中str的值后,比较pt和pt1的str的值:");  
  59.   pt1.setStr("你好,世界");  
  60.   System.out.println(pt.getStr());  
  61.   System.out.println(pt1.getStr());  
  62.   System.out.println("============================");  
  63.   System.out.println("比较pt和pt1中temp对象的值");  
  64.   System.out.println(pt.getTemp());  
  65.   System.out.println(pt1.getTemp());  
  66.     
  67.   System.out.println("使用深克隆方法进行建立对象");  
  68.   System.out.println("============================");  
  69.   pt1=(Prototype)pt.deepClone();  
  70.   System.out.println(pt.getTemp());  
  71.   System.out.println(pt1.getTemp());  
  72.     
  73.     
  74.   
  75.  }  
  76.   
  77. }  


输出结果:
使用浅克隆方法进行建立对象
=============================
比较ptpt1str的值:
Hello World
Hello World
修改pt1对象中str的值后,比较ptpt1str的值:
Hello World
你好,世界
============================
比较ptpt1temp对象的值
Temp@affc70
Temp@affc70
使用深克隆方法进行建立对象
============================
Temp@affc70
Temp@15d56d5

 从上面的输出结果咱们能够看出使用Object.clone()方法只能浅层次的克隆,即只能对那些成员变量是基本类型或String类型的对象进行克隆,对哪些成员变量是类类型的对象进行克隆要使用到对象的序列化,否则克隆克隆出来的Prototype对象都共享同一个temp实例。

总结:

    原型模式做为建立型模式中的最特殊的一个模式,具体的建立过程,是由对象自己提供,这样咱们在不少的场景下,咱们能够很方便的快速的构建新

的对象,就像前面分析讲解的几类场景中,可能咱们经过使用对象的克隆,比经过其余几类的建立型模式,效果要好的多,并且代价也小不少。打个比方,

原型模式对于系统的扩展,能够作到无缝的扩展,为何这么说呢?好比其余的建立型工厂,若是新增一个对象类型,那么咱们不论是修改配置文件的方

式,仍是修改代码的形式,无疑咱们都是须要进行修改的,对于咱们你们通用的公共应用来讲这无疑是危险的,那么经过原型模式,则能够解决这样的问

题,由于类型自己实现这样的方法便可,可是也有必定的缺点,每一个对象都实现这样的方法,无疑是很大的工做量,可是在某些特殊的环境下,或者实际的

项目中,可能原型模式是好的选择。

相关文章
相关标签/搜索