標籤:
這篇部落格我們來介紹一下建造者模式(Builder Pattern),建造者模式又被稱為產生器模式,是創造性模式之一,與Factory 方法模式和抽象原廠模式不同,後兩者的目的是為了實現多態性,而 Builder 模式的目的則是為了將對象的構建與展示分離。Builder 模式是一步一步建立一個複雜物件的建立型模式,它允許使用者在不知道內部構建細節的情況下,可以更精細地控制對象的構造流程。一個複雜的對象有大量的組成部分,比如汽車它有車輪、方向盤、發動機、以及各種各樣的小零件,要將這些組件裝配成一輛汽車,這個裝配過程無疑會複雜,對於這種情況,為了實現在構建過程中對外部隱藏具體細節,就可以使用 Builder 模式將組件和組裝過程分離,使得構建過程和組件都可以自由擴充,同時也能夠將兩者之間的耦合降到最低。
轉載請註明出處:http://blog.csdn.net/self_study/article/details/51707029。
PS:對技術感興趣的同鞋加群544645972一起交流。
設計模式總目錄
java/android 設計模式學習筆記目錄
特點
將一個複雜物件的構建和它的表示分離,使得同樣的構建過程可以建立不同的表示。Builder 模式適用的使用情境:
- 相同的方法,不同的執行順序,產生不同的事件結果;
- 多個組件或零件,都可以裝配到一個對象中,但是產生的運行結果又不相同時;
- 產品類非常複雜,或者產品類中的調用順序不同產生不同的作用,這個時候使用建造者模式非常適合;
- 當初始化一個對象特別複雜,如參數多,且很多參數都具有預設值時。
在實際開發過程中,Builder 模式可以使用原廠模式或者任一其他建立型模式去產生自己的指定組件,Builder 模式也是實現 fluent interface 一種非常好的方法。
UML類圖
Builder 模式的 uml 類圖如下所示:
有四個角色:
- Product 產品模組
產品的相關類;
- Builder 介面或抽象類別
規範產品的組件,一般是由子類實現具體的組件過程,需要注意的是這個角色在實際使用過程中可以省略,最典型的就是像 AlertDialog.Builder 一樣,省略 Builder 虛擬類,將 ConcreteBuilder 寫成一個靜態內部類;
- ConcreateBuilder 類
具體的 Builder 類;
- Director 類
統一組裝過程,同樣值得注意的是,在現實開發過程中,Director 角色也經常會被省略,而直接使用一個 Builder 來進行對象的組裝,這個 Builder 通常為鏈式調用,也就是上面提到的 fluent interface ,它的關鍵點是每個 setter 方法都返回自身,也就是 return this,這樣就使得 setter 方法可以鏈式調用,最典型的仍然是 AlertDialog.Builder 類,使用這種方式不僅去除了 Director 角色,使得整個結構更加簡單,也能對 Product 對象的組件過程有著更精細的控制。
據此我們可以寫出 Builder 模式的通用代碼:
Product.class
public class Product { public int partB; public int partA; public int getPartA() { return partA; } public void setPartA(int partA) { this.partA = partA; } public int getPartB() { return partB; } public void setPartB(int partB) { this.partB = partB; } @Override public String toString() { return "partA : " + partA + " partB : " + partB; }}
產品類在此聲明了兩個 setter 方法,然後是 Builder 相關類:
Builder.class
public abstract class Builder { public abstract void buildPartA(int partA); public abstract void buildPartB(int partB); public abstract Product build();}
ConcreteBuilder.class
public class ConcreteBuilder extends Builder{ private Product product = new Product(); @Override public void buildPartA(int partA) { product.setPartA(partA); } @Override public void buildPartB(int partB) { product.setPartB(partB); } @Override public Product build() { return product; }}
Builder 這兩個類用來封裝對 Product 屬性的設定,最後在 build 方法中返回設定完屬性的 Product 對象,最後是 Director 角色:
Director.class
public class Director { private Builder builder; public Director(Builder builder) { this.builder = builder; } public void construct(int partA, int partB) { builder.buildPartA(partA); builder.buildPartB(partB); }}
封裝了 Builder 對象,最後是測試程式:
Builder builder = new ConcreteBuilder();Director director = new Director(builder);director.construct(1, 2);Product product = builder.build();Log.e("shawn", product.toString());break;
運行結果
com.android.builderpattern E/shawn: partA : 1 partB : 2
代碼一目瞭然,這裡需要提到的一點是針對不同的產品可以去構建不同的 ConcreteBuilder 類,使得一個 ConcreteBuilder 類對應一個 Product 類,這點和Factory 方法模式很類似,我們後面也會介紹到他們兩者之間的區別。
樣本與源碼
Builder 模式在實際開發中出現和使用的頻率也是很高的,比如上面提到的 AlertDialog.Builder ,還比如非常有名的第三方開源架構 Universal-Image-Loader 庫中的 ImageLoaderConfig ,他們都是使用的靜態內部 Builder 類。
這裡的 demo 也使用最簡單的內部靜態 Builder 類去實現,精簡完之後只有 ConcreteBuilder 和 Product 角色,並且使用鏈式調用去實現上面提到的 fluent interface:
Computer.class
public class Computer { private String CPU; private String GPU; private String memoryType; private int memorySize; private String storageType; private int storageSize; private String screenType; private float screenSize; private String OSType; public static class Builder { // Optional parameters - initialize with default values private String CPU = "inter-i3"; private String GPU = "GTX-960"; private String memoryType = "ddr3 1666MHz"; private int memorySize = 8;//8GB private String storageType = "hdd"; private int storageSize = 1024;//1TB private String screenType = "IPS"; private float screenSize = 23.8f; private String OSType = "Windows 10"; public Builder() { } public Builder setCPU(String CPU) { this.CPU = CPU; return this; } public Builder setGPU(String GPU) { this.GPU = GPU; return this; } public Builder setMemoryType(String memoryType) { this.memoryType = memoryType; return this; } public Builder setMemorySize(int memorySize) { this.memorySize = memorySize; return this; } public Builder setStorageType(String storageType) { this.storageType = storageType; return this; } public Builder setStorageSize(int storageSize) { this.storageSize = storageSize; return this; } public Builder setScreenType(String screenType) { this.screenType = screenType; return this; } public Builder setScreenSize(float screenSize) { this.screenSize = screenSize; return this; } public Builder setOSType(String OSType) { this.OSType = OSType; return this; } public Computer create() { return new Computer(this); } } private Computer(Builder builder) { CPU = builder.CPU; GPU = builder.GPU; memoryType = builder.memoryType; memorySize = builder.memorySize; storageType = builder.storageType; storageSize = builder.storageSize; screenType = builder.screenType; screenSize = builder.screenSize; OSType = builder.OSType; }}
Computer 為產品類,它有一個 Builder 的靜態內部類用於設定相關屬性,測試代碼:
Computer computer = new Computer.Builder() .setCPU("inter-skylake-i7") .setGPU("GTX-Titan") .setMemoryType("ddr4-2133MHz") .setMemorySize(16) .setStorageType("ssd") .setStorageSize(512) .setScreenType("IPS") .setScreenSize(28) .setOSType("Ubuntu/Window10") .create();
這裡需要提到的關鍵點是關於相關屬性的預設值問題:
- 對於必要的屬性值,無法給出其預設值的最好是通過 Builder 類的建構函式傳入,比如 AlertDialog.Builder 類的 Context,這樣也能防止使用時的疏忽;
- 對於非必要屬性來說,最好是為其產生一個預設的屬性值,這樣使用者只用設定需要更改的屬性即可;
- 每個 setter 函數都加上 return this 用來實現優美的 fluent interface 設計。
總結
Builder 模式在 Android 開發中也很常用,通常作為配置類的構建器將配置的構建和表示分離開來,同時也將配置從目標類中隔離開來,避免了過多的 setter 方法。Builder 模式比較常見的實現形式是通過調用鏈實現,這樣的方式也會使得代碼更加簡潔和易懂,而且同時也可以避免了目標類被過多的介面“汙染”。
Builder 模式的優點:
- 將一個複雜物件的建立過程封裝起來,使得用戶端不必知道產品內部組成的細節;
- 允許對象通過多個步驟來建立,並且可以改變過程和選擇需要的過程;
- 產品的實現可以被替換,因為用戶端只看到一個抽象的介面;
- 建立者獨立,容易擴充。
Builder 模式缺點:
- 會產生多餘的 Builder 對象以及 Director 對象,消耗記憶體;
- 與原廠模式相比,採用 Builder 模式建立對象的客戶,需要具備更多的領域知識。
Builder 模式 VS Factory 方法模式
Builder 模式和Factory 方法模式都是屬於建立型模式,他們有一些共同點:這兩種設計模式的都將一個產品類對象的建立過程封裝起來,讓用戶端從具體產品類的產生中解耦,不必瞭解產品類構造的細節。但是其實他們兩種設計模式還是有很多不同點:
- Builder 模式允許對象的建立通過多個步驟來建立,而且可以改變這個過程,也可以選擇需要改變的屬性;Factory 方法模式不一樣,它只有一個步驟,也就無法改變這個過程,更加無法選擇性改變屬性了;
- Builder 模式的目的是將複雜物件的構建和它的表示分離;而Factory 方法模式則是定義一個建立對象的介面,由子類決定要執行個體化的類是哪一個,將執行個體化延遲到子類;
- 最明顯的當然還是代碼的差異,Builder 模式中用戶端可以調用 set 方法,而Factory 方法模式只能調用工廠類提供的相關方法。
其次是 uml 類圖的差異:
Builder 模式 uml 類圖:
註:Director 類和 Builder 虛擬類可以被精簡。
Factory 方法模式 uml 類圖:
uml 類圖的相似性還是很高的,所以通常我們會根據實際表現和用途來區別 Buidler 模式和Factory 方法模式(這點和裝飾者模式與保護代理模式的區別類似,要從實際表現與使用的目的區別)。
源碼下載
https://github.com/zhaozepeng/Design-Patterns/tree/master/BuilderPattern
引用
http://blog.csdn.net/jason0539/article/details/44992733
https://en.wikipedia.org/wiki/Builder_pattern
java/android 設計模式學習筆記(10)---建造者模式