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

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

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

意图:将一个复杂对象的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。

类图:

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

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

适用范围:

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

2.需要生成的产品对象的属性相互依赖,建造者模式可以强迫生成顺序。

3.在对象创建过程中会使用到系统中的一些其他对象,这些对象在产品对象的创建过程中不易得到。

效果:

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()操作就可以快速完成对象的创建,这无疑是一种非常有效的方式,快速的创建一个新的对象。

原型模式的原理图:

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

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

主要运用场合:

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

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

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

深复制和浅复制:

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

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

源码 打印 ?
  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实例。

总结:

    原型模式作为创建型模式中的最特殊的一个模式,具体的创建过程,是由对象本身提供,这样我们在很多的场景下,我们可以很方便的快速的构建新

的对象,就像前面分析讲解的几类场景中,可能我们通过使用对象的克隆,比通过其他几类的创建型模式,效果要好的多,而且代价也小很多。打个比方,

原型模式对于系统的扩展,可以做到无缝的扩展,为什么这么说呢?比如其他的创建型工厂,如果新增一个对象类型,那么我们不管是修改配置文件的方

式,还是修改代码的形式,无疑我们都是需要进行修改的,对于我们大家通用的公共应用来说这无疑是危险的,那么通过原型模式,则可以解决这样的问

题,因为类型本身实现这样的方法即可,但是也有一定的缺点,每个对象都实现这样的方法,无疑是很大的工作量,但是在某些特殊的环境下,或者实际的

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

你可能感兴趣的:(设计模式)