咱們都知道設計模式分爲建立型,結構型和行爲型。建立型有,單例模式,工廠模式,建造者模式和原型模式。設計模式
今天,咱們再來學習另一個比較經常使用的建立型設計模式,Builder 模式,中文翻譯爲建造者模式或者構建者模式,也有人叫它生成器模式。函數
不少博客總結的關於建造者模式的做用是:建立複雜對象的時候,用建造者模式可使客戶端沒必要知道產品內部組成的細節。 這雖然是一個很重要的特徵,可是還有一個特徵以及做用不少人都並不知道。咱們接下來就來好好的探討一番。學習
在平時的開發中,建立一個對象最經常使用的方式是,使用 new 關鍵字調用類的構造函數來完成。雖然這是最簡單最經常使用的,可是咱們仔細想想真的是全部場景都適用嗎?ui
有以下的一個場景: 有一個工廠類,這個工廠類有以下幾個成員變量: 1. 工廠名字 2. 工廠員工列表 3. 工廠設備列表
要想讓這個工廠正常的運行,這3個成員變量必須被正確賦值。那麼此時,咱們最多見的方法就是在構造方法中實現這些成員變量的賦值。this
當成員變量很少的時候,像上訴說的3個,這樣並無什麼問題,可是當成員變量變成6個,12個甚至更多,那繼續沿用如今的設計思路,構造函數的參數列表會變得很長,代碼在可讀性和易用性上都會變差。在使用構造函數的時候,咱們就容易搞錯各參數的順序,傳遞進錯誤的參數值,致使很是隱蔽的 bug。翻譯
解決這個問題的辦法你應該也已經想到了,那就是用 set() 函數來給成員變量賦值,以替代冗長的構造函數。咱們將必填的成員變量,放在咱們的構造方法中,強制建立類對象的時候就要填寫。將其餘不是必填的成員變量咱們經過 set() 函數來設置,讓使用者自主選擇填寫或者不填寫。設計
這樣代碼在可讀性和易用性上提升了不少。code
當咱們的以下幾個需求的時候,上訴使用set()函數的設計思路可能就不太知足了對象
爲了解決這些問題,建造者模式就派上用場了。繼承
咱們能夠把校驗邏輯放置到 Builder 類中,先建立建造者,而且經過 set() 方法設置建造者的變量值,而後在使用 build() 方法真正建立對象以前,作集中的校驗,校驗經過以後纔會建立對象。除此以外,咱們把 Factory 的構造函數改成 private 私有權限。這樣咱們就只能經過建造者來建立 Factory 類對象。而且,Factory 沒有爲不可變屬性提供任何 set() 方法,這樣咱們建立出來的對象就作到了相對不可。代碼以下:
public class Factory { private String factoryName; //工廠名字 private List<Integer> employeeIds; //員工列表 private Map<Integer,Integer> salaryMap; //員工工資 private List<String> equipmentName; //設備列表 //私有構造方法 private Factory(){} private Factory(Builder builder) { this.factoryName = builder.factoryName; this.employeeIds = builder.employeeIds; this.salaryMap = builder.salaryMap; this.equipmentName = builder.equipmentName; } //...省略getter方法... //咱們將Builder類設計成了Factory的內部類。 //咱們也能夠將Builder類設計成獨立的非內部類FactoryBuilder。 public static class Builder { private String factoryName; private List<Integer> employeeIds; private Map<Integer,Integer> salaryMap; private List<String> equipmentName; public Factory build() { // 校驗邏輯放到這裏來作,包括必填項校驗、依賴關係校驗、約束條件校驗等 if (StringUtils.isBlank(factoryName)) { throw new IllegalArgumentException("..."); } for(Integer employeeId : employeeIds){ if (salaryMap.get(employeeId) == null) { throw new IllegalArgumentException("..."); } } return new ResourcePoolConfig(this); } public Builder setFactoryName(String factoryName) { if (StringUtils.isBlank(factoryName)) { throw new IllegalArgumentException("..."); } this.factoryName = factoryName; return this; } public Builder setEmployeeIds(List<Integer> employeeIds) { // 員工不能爲0 if (employeeIds.size() == 0) { throw new IllegalArgumentException("..."); } this.employeeIds = employeeIds; return this; } public Builder setSalaryMap(Map<Integer,Integer> salaryMap) { if (salaryMap.size() == 0) { throw new IllegalArgumentException("..."); } this.salaryMap = salaryMap; return this; } public Builder setEquipmentName(List<String> equipmentName) { // 設備必須大於兩個 if (equipmentName.siez() < 2) { throw new IllegalArgumentException("..."); } this.minIdle = minIdle; return this; } } } // 這段代碼會拋出IllegalArgumentException,由於設備只有一臺 List<Integer> employeeIds = Collections.singletonList(1); Map<Integer, Integer> salaryMap = Collections.singletonMap(1, 9000); List<String> equipmentName = Collections.singletonList("新型設備"); Factory factory = new Factory.Builder() .setFactoryName("萬能工廠") .setEmployeeIds(employeeIds) .setSalaryMap(salaryMap) .setEquipmentName(equipmentName) .build();
咱們來與工廠模式作一個對比,建造者模式是讓建造者類來負責對象的建立工做。工廠模式,是由工廠類來負責對象建立的工做。
那它們之間有什麼區別呢?實際上,工廠模式是用來建立不一樣可是相關類型的對象(繼承同一父類或者接口的一組子類),由給定的參數來決定建立哪一種類型的對象。
建造者模式是用來建立一種類型的複雜對象,經過設置不一樣的可選參數,「定製化」地建立不一樣的對象。網上有一個經典的例子很好地解釋了二者的區別。
顧客走進一家餐館點餐,咱們利用工廠模式,根據用戶不一樣的選擇,來製做不一樣的食物,好比披薩、漢堡、沙拉。對於披薩來講,用戶又有各類配料能夠定製,好比奶酪、西紅柿、起司,咱們經過建造者模式根據用戶選擇的不一樣配料來製做披薩。
實際上,咱們也不要太學院派,非得把工廠模式、建造者模式分得那麼清楚,咱們須要知道的是,每一個模式爲何這麼設計,能解決什麼問題。只有瞭解了這些最本質的東西,咱們才能不生搬硬套,才能靈活應用,甚至能夠混用各類模式創造出新的模式,來解決特定場景的問題。