長平狐 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實例。

總結:

    原型模式做爲建立型模式中的最特殊的一個模式,具體的建立過程,是由對象自己提供,這樣咱們在不少的場景下,咱們能夠很方便的快速的構建新

的對象,就像前面分析講解的幾類場景中,可能咱們經過使用對象的克隆,比經過其餘幾類的建立型模式,效果要好的多,並且代價也小不少。打個比方,

原型模式對於系統的擴展,能夠作到無縫的擴展,爲何這麼說呢?好比其餘的建立型工廠,若是新增一個對象類型,那麼咱們不論是修改配置文件的方

式,仍是修改代碼的形式,無疑咱們都是須要進行修改的,對於咱們你們通用的公共應用來講這無疑是危險的,那麼經過原型模式,則能夠解決這樣的問

題,由於類型自己實現這樣的方法便可,可是也有必定的缺點,每一個對象都實現這樣的方法,無疑是很大的工做量,可是在某些特殊的環境下,或者實際的

項目中,可能原型模式是好的選擇。

相關文章
相關標籤/搜索