我們都知道設計模式分為建立型,結構型和行為型。建立型有,單例模式,工廠模式,建造者模式和原型模式。
今天,我們再來學習另外乙個比較常用的建立型設計模式,builder 模式,中文翻譯為建造者模式或者構建者模式,也有人叫它生成器模式。
很多部落格總結的關於建造者模式的作用是:建立複雜物件的時候,用建造者模式可以使客戶端不必知道產品內部組成的細節。這雖然是乙個很重要的特徵,但是還有乙個特徵以及作用很多人都並不知道。我們接下來就來好好的**一番。
在平時的開發中,建立乙個物件最常用的方式是,使用 new 關鍵字呼叫類的建構函式來完成。雖然這是最簡單最常用的,但是我們仔細想一想真的是所有場景都適用嗎?
有如下的乙個場景:
有乙個工廠類,這個工廠類有如下幾個成員變數:
1. 工廠名字
2. 工廠員工列表
3. 工廠裝置列表
要想讓這個工廠正常的執行,這3個成員變數必須被正確賦值。那麼此時,我們最常見的方法就是在構造方法中實現這些成員變數的賦值。
當成員變數不多的時候,像上訴說的3個,這樣並沒有什麼問題,但是當成員變數變成6個,12個甚至更多,那繼續沿用現在的設計思路,建構函式的引數列表會變得很長,**在可讀性和易用性上都會變差。在使用建構函式的時候,我們就容易搞錯各引數的順序,傳遞進錯誤的引數值,導致非常隱蔽的 bug。
解決這個問題的辦法你應該也已經想到了,那就是用 set() 函式來給成員變數賦值,以替代冗長的建構函式。我們將必填的成員變數,放在我們的構造方法中,強制建立類物件的時候就要填寫。將其他不是必填的成員變數我們通過 set() 函式來設定,讓使用者自主選擇填寫或者不填寫。
這樣**在可讀性和易用性上提高了很多。
當我們的如下幾個需求的時候,上訴使用set()函式的設計思路可能就不太滿足了
我們剛剛說的,將必填的成員變數放到構造方法。如果必填項很多,那構造方法又會出現我們之前說的那個問題。但是,如果我們把必填項也通過 set() 方法設定,那校驗這些必填項是否已經填寫的邏輯就無處安放了。
除此之外,假設配置項之間有一定的依賴關係,比如,如果使用者設定了員工列表, 就必須顯式地設定員工工資;或者配置項之間有一定的約束條件,比如,員工和工資必須一一對應。如果我們繼續使用現在的設計思路,那這些配置項之間的依賴關係或者約束條件的校驗邏輯就無處安放了。
如果我們希望這個工廠類物件某些屬性如name是不可變的,也就是說,物件在建立好之後,就不能再修改這些內部的屬性值。要實現這個功能,我們就不能在外部暴露 set() 方法。
為了解決這些問題,建造者模式就派上用場了。
我們可以把校驗邏輯放置到 builder 類中,先建立建造者,並且通過 set() 方法設定建造者的變數值,然後在使用 build() 方法真正建立物件之前,做集中的校驗,校驗通過之後才會建立物件。除此之外,我們把 factory 的建構函式改為 private 私有許可權。這樣我們就只能通過建造者來建立 factory 類物件。並且,factory 沒有為不可變屬性提供任何 set() 方法,這樣我們建立出來的物件就做到了相對不可。**如下:
public class factory
private factory(builder builder)
//...省略getter方法...
//我們將builder類設計成了factory的內部類。
//我們也可以將builder類設計成獨立的非內部類factorybuilder。
public static class builder
for(integer employeeid : employeeids)
}return new resourcepoolconfig(this);
}public builder setfactoryname(string factoryname)
this.factoryname = factoryname;
return this;
}public builder setemployeeids(listemployeeids)
this.employeeids = employeeids;
return this;
}public builder setsalarymap(mapsalarymap)
this.salarymap = salarymap;
return this;
}public builder setequipmentname(listequipmentname)
this.minidle = minidle;
return this;}}}
// 這段**會丟擲illegalargumentexception,因為裝置只有一台
listemployeeids = collections.singletonlist(1);
mapsalarymap = collections.singletonmap(1, 9000);
listequipmentname = collections.singletonlist("新型裝置");
factory factory = new factory.builder()
.setfactoryname("萬能工廠")
.setemployeeids(employeeids)
.setsalarymap(salarymap)
.setequipmentname(equipmentname)
.build();
我們來與工廠模式做乙個對比,建造者模式是讓建造者類來負責物件的建立工作。工廠模式,是由工廠類來負責物件建立的工作。
那它們之間有什麼區別呢?實際上,工廠模式是用來建立不同但是相關型別的物件(繼承同一父類或者介面的一組子類),由給定的引數來決定建立哪種型別的物件。
建造者模式是用來建立一種型別的複雜物件,通過設定不同的可選引數,「定製化」地建立不同的物件。網上有乙個經典的例子很好地解釋了兩者的區別。
顧客走進一家餐館點餐,我們利用工廠模式,根據使用者不同的選擇,來製作不同的食物,比如披薩、漢堡、沙拉。對於披薩來說,使用者又有各種配料可以定製,比如乳酪、西紅柿、起司,我們通過建造者模式根據使用者選擇的不同配料來製作披薩。
實際上,我們也不要太學院派,非得把工廠模式、建造者模式分得那麼清楚,我們需要知道的是,每個模式為什麼這麼設計,能解決什麼問題。只有了解了這些最本質的東西,我們才能不生搬硬套,才能靈活應用,甚至可以混用各種模式創造出新的模式,來解決特定場景的問題。
設計模式 建造者模式
在gof的23種設計模式中對builder pattern的定義是 將乙個複雜物件的構建與它的表示分離,使得同樣的構建過程可以建立不同的表示。從程式角度來說,就是在基類定義某種事物建立的過程或業務流程,在子類進行重寫或是使用基類方法。這樣建立出來的例項不會因為過程或流程的丟失而使業務失敗。舉例來說,...
設計模式建造者模式
今天看了乙個設計模式。總結下,以免忘了。如何引入建造者模式的呢?作者舉了個例子。建造小人。當然需要 頭,左手,右手,左腳,右腳,軀幹。建造的過程使用 使用類裡面的方法就行了。但是如果需要乙個胖的小人,乙個瘦的小人呢?就需要重新構造乙個胖人類,乙個瘦人類。當然 使用者還需要自己處理構造的過程。這樣 就...
設計模式 建造者模式
定義 將乙個複雜的物件的構建與它的表示分離,是的同樣的構建過程可以建立不同的表示。包括的要素 1 產品類 2 抽象建造者或者介面 3 建造者 4 導演類 建造者模式的優點 首先,建造者模式的封裝很好,使用建造者模式可以有效地封裝變化,注意在使用建造者模式的場景中,一般產品類和建造者介面是比較穩定的,...