本文來自:曹勝歡部落格專欄。轉載請註明出處:http://blog.csdn.net/csh624366188
建立者模式是建立型模式中最負責的一個設計模式了,建立者負責構建一個對象的各個部分,並且完成組裝的過程.構建模式主要用來針對複雜產品生產,分離組件構建細節,以達到良好的伸縮性。把構造對象執行個體的邏輯移到了類的外部,在這個類外部定義了這個類的構造邏輯。它把一個複雜物件的構過程從對象的表示中分離出來。其直接效果是將一個複雜的對象簡化為一個比較簡單的對象。它強調的是產品的構造過程。
在軟體系統中,有時候面臨著“一個複雜物件”的建立工作,其通常由各個部分的子物件用一定的演算法構成;由於需求的變化,這個複雜物件的各個部分經常面臨著劇烈的變化,但是將它們組合在一起的演算法確相對穩定。如何應對這種變化?如何提供一種“封裝機制”來隔離出“複雜物件的各個部分”的變化,從而保持系統中的“穩定構建演算法”不隨著需求改變而改變?這就是要說的建造者模式。
意圖:將一個複雜物件的構建與其表示相分離,使得同樣的構建過程可以建立不同的表示。
類圖:
抽象建造者(Builder):給出一個抽象介面,以規範產品對象的各個組成成分的建造。這個介面規定要實現複雜物件的哪些部分的建立,並不涉及具體的對象組件的建立。
具體建造者(Concrete Builder):實現Builder介面,針對不同的商業邏輯,具體化複雜物件的各部分的建立。 在建造過程完成後,提供產品的執行個體。
指導者(Director):調用具體建造者來建立複雜物件的各個部分,在指導者中不涉及具體產品的資訊,只負責保證對象各部分完整建立或按某種順序建立。
產品(Product):要建立的複雜物件。
適用範圍:
1.需要產生的產品對象有複雜的內部結構。
2.需要產生的產品對象的屬性相互依賴,建造者模式可以強迫產生順序。
3.在對象建立過程中會使用到系統中的一些其他對象,這些對象在產品對象的建立過程中不易得到。
效果:
1.建造者模式的使用時的產品的內部表象可以獨立的變化。使用建造者模式可以使用戶端不必知道產品內部組成的細節。
2.每一個Builder都相對獨立,而與其他的Builder無關。
3.模式所建造的最終產品易於控制。
下面我們來看一下經典建立者模式的一個實現形式,然後針對這個傳統模式後面提出幾個改進方案,我們這裡以上面講述的服裝的過程作為例子來說明下建立者模式的原理和思想,希望大家也能靈活的運用到實際的項目中去。達到學以致用的目的。
我們來看看具體的代碼實現:
package builder;public class Product {ArrayList<String> parts = new ArrayList<String>();public void add(String part){parts.add(part);}public void show(){System.out.println("產品建立------------");for(String part : parts){System.out.println(part);}}}public abstract class Builder {public abstract void BuildPartA();public abstract void BuildPartB();public abstract Product getResult();}public class ConcreteBuilder1 extends Builder{private Product product = new Product();@Overridepublic void BuildPartA() {product.add("組件A");}@Overridepublic void BuildPartB() {product.add("組件B");}@Overridepublic Product getResult() {return product;}}public class ConcreteBuilder2 extends Builder{private Product product = new Product();@Overridepublic void BuildPartA() {product.add("組件x");}@Overridepublic void BuildPartB() {product.add("組件y");}@Overridepublic Product getResult() {return product;}}public class Director {public void Construct(Builder builder){builder.BuildPartA();builder.BuildPartB();}}public class TestBuilder {public static void main(String[] args) {Director director = new Director();Builder b1 = new ConcreteBuilder1();Builder b2 = new ConcreteBuilder2();director.Construct(b1);Product p1 = b1.getResult();p1.show();director.Construct(b2);Product p2 = b2.getResult();p2.show();}}
通過上面的代碼,我們給出了經典建立者模式的核心代碼形式,那麼針對上面無疑有以下的幾個缺點:
1、Ibuilder介面必須定義完整的組裝流程,一旦定義就不能隨意的動態修改。
2、Builder與具體的對象之間有一定的依賴關係,當然這裡可以通過介面來解耦來實現靈活性。
3、Builder必須知道具體的流程。
那麼針對上面的幾個問題,我們如何來解決呢?我想前面的建立型模式已經給我了足夠的經驗,還是通過設定檔或者其他的形式來提供靈活性。
原型模式
用原型執行個體指定建立對象的種類,並且通過拷貝這些原型建立新的對象。 Prototype原型模式是一種建立型設計模式,Prototype模式允許一個對象再建立另外一個可定製的對象,根本無需知道任何如何建立的細節,工作原理是:通過將一個原型對象傳給那個要發動建立的對象,這個要發動建立的對象通過請求原型對象拷貝它們自己來實施建立。它主要面對的問題是:“某些結構複雜的對象”的建立工作;由於需求的變化,這些對象經常面臨著劇烈的變化,但是他們卻擁有比較穩定一致的介面。
原型模式最大的特點是複製一個現有的對象,這個複製的結果有2種,一種是是淺複製,另一種是深複製,這裡我們也會探討下深複製和淺複製的原理,這樣可能更方便大家理解這個原型模式的使用。我們都知道,建立型模式一般是用來建立一個新的對象,然後我們使用這個對象完成一些對象的操作,我們通過原型模式可以快速的建立一個對象而不需要提供專門的new()操作就可以快速完成對象的建立,這無疑是一種非常有效方式,快速的建立一個新的對象。
原型模式的原理圖:
原型模式的主要思想是基於現有的對象複製一個新的對象出來,一般是有對象的內部提供複製的方法,通過該方法返回一個對象的副本,這種建立對象的方式,相比我們之前說的幾類建立型模式還是有區別的,之前的講述的原廠模式與抽象工廠都是通過工廠封裝具體的new操作的過程,返回一個新的對象,有的時候我們通過這樣的建立工廠建立對象不值得,特別是以下的幾個情境的時候,可能使用原型模式更簡單也效率更高。
主要運用場合:
1、如果說我們的物件類型不是剛開始就能確定,而是這個類型是在運行期確定的話,那麼我們通過這個類型的對象複製出一個新的類型更容易。
2、有的時候我們可能在實際的項目中需要一個對象在某個狀態下的副本,這個前提很重要,這點怎麼理解呢,例如有的時候我們需要對比一個對象經過處理後的狀態和處理前的狀態是否發生過改變,可能我們就需要在執行某段處理之前,複製這個對象此時狀態的副本,然後等執行後的狀態進行相應的對比,這樣的應用在項目中也是經常會出現的。
3、當我們在處理一些對象比較簡單,並且對象之間的區別很小,可能只是很固定的幾個屬性不同的時候,可能我們使用原型模式更合適
深複製和淺複製:
⑴淺複製(淺複製)
被複製對象的所有變數都含有與原來的對象相同的值,而所有的對其他對象的引用仍然指向原來的對象。換言之,淺複製僅僅複製所考慮的對象,而不複製它所引用的對象。
⑵深複製(深複製)
被複製對象的所有變數都含有與原來的對象相同的值,除去那些引用其他對象的變數。那些引用其他對象的變數將指向被複製過的新對象,而不再是原有的那些被引用的對象。換言之,深複製把要複製的對象所引用的對象都複製了一遍。
下面舉一個執行個體來實現以下原型模式:
import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.io.Serializable; class Prototype implements Cloneable,Serializable{ private String str; private Temp temp; public Object clone()throws CloneNotSupportedException{ //淺複製 Prototype prototype=(Prototype)super.clone(); return prototype; } public Object deepClone()throws IOException,ClassNotFoundException{ //深複製 ByteArrayOutputStream bo=new ByteArrayOutputStream(); ObjectOutputStream oo=new ObjectOutputStream(bo); oo.writeObject(this); ByteArrayInputStream bi=new ByteArrayInputStream(bo.toByteArray()); ObjectInputStream oi=new ObjectInputStream(bi); return oi.readObject(); } public String getStr() { return str; } public void setStr(String str) { this.str = str; } public Temp getTemp() { return temp; } public void setTemp(Temp temp) { this.temp = temp; } }class Temp implements Serializable{ }public class Test { public static void main(String[] args)throws CloneNotSupportedException,ClassNotFoundException ,IOException{ Prototype pt=new Prototype(); Temp temp=new Temp(); pt.setTemp(temp); pt.setStr("Hello World"); System.out.println("使用淺複製方法進行建立對象"); Prototype pt1=(Prototype)pt.clone(); System.out.println("============================="); System.out.println("比較pt和pt1的str的值:"); System.out.println(pt.getStr()); System.out.println(pt1.getStr()); System.out.println("修改pt1對象中str的值後,比較pt和pt1的str的值:"); pt1.setStr("你好,世界"); System.out.println(pt.getStr()); System.out.println(pt1.getStr()); System.out.println("============================"); System.out.println("比較pt和pt1中temp對象的值"); System.out.println(pt.getTemp()); System.out.println(pt1.getTemp()); System.out.println("使用深複製方法進行建立對象"); System.out.println("============================"); pt1=(Prototype)pt.deepClone(); System.out.println(pt.getTemp()); System.out.println(pt1.getTemp()); }}
輸出結果:
使用淺複製方法進行建立對象
=============================
比較pt和pt1的str的值:
Hello World
Hello World
修改pt1對象中str的值後,比較pt和pt1的str的值:
Hello World
你好,世界
============================
比較pt和pt1中temp對象的值
Temp@affc70
Temp@affc70
使用深複製方法進行建立對象
============================
Temp@affc70
Temp@15d56d5
從上面的輸出結果我們可以看出使用Object.clone()方法只能淺層次的複製,即只能對那些成員變數是基本類型或String類型的對象進行複製,對哪些成員變數是類類型的對象進行複製要使用到對象的序列化,不然複製複製出來的Prototype對象都共用同一個temp執行個體。
總結:
原型模式作為建立型模式中的最特殊的一個模式,具體的建立過程,是由對象本身提供,這樣我們在很多的情境下,我們可以很方便的快速的構建新
的對象,就像前面分析講解的幾類情境中,可能我們通過使用對象的複製,比通過其他幾類的建立型模式,效果要好的多,而且代價也小很多。打個比方,
原型模式對於系統的擴充,可以做到無縫的擴充,為什麼這麼說呢?比如其他的建立型工廠,如果新增一個物件類型,那麼我們不管是修改設定檔的方
式,還是修改代碼的形式,無疑我們都是需要進行修改的,對於我們大家通用的公用應用來說這無疑是危險的,那麼通過原型模式,則可以解決這樣的問
題,因為類型本身實現這樣的方法即可,但是也有一定的缺點,每個對象都實現這樣的方法,無疑是很大的工作量,但是在某些特殊的環境下,或者實際的
項目中,可能原型模式是好的選擇。