建造者模式(Builder Pattern)使用多個簡單的對象一步一步構建成一個複雜的對象。這種類型的設計模式屬於建立型模式,它提供了一種建立對象的最佳方式。html
一個 Builder 類會一步一步構造最終的對象。該 Builder 類是獨立於其餘對象的。java
意圖:將一個複雜的構建與其表示相分離,使得一樣的構建過程能夠建立不一樣的表示。算法
主要解決:主要解決在軟件系統中,有時候面臨着"一個複雜對象"的建立工做,其一般由各個部分的子對象用必定的算法構成;因爲需求的變化,這個複雜對象的各個部分常常面臨着劇烈的變化,可是將它們組合在一塊兒的算法卻相對穩定。編程
什麼時候使用:一些基本部件不會變,而其組合常常變化的時候。設計模式
如何解決:將變與不變分離開。ide
關鍵代碼:建造者:建立和提供實例,導演:管理建造出來的實例的依賴關係。函數
應用實例: 一、去肯德基,漢堡、可樂、薯條、炸雞翅等是不變的,而其組合是常常變化的,生成出所謂的"套餐"。 二、JAVA 中的 StringBuilder。測試
優勢: 一、建造者獨立,易擴展。 二、便於控制細節風險。ui
缺點: 一、產品必須有共同點,範圍有限制。 二、如內部變化複雜,會有不少的建造類。this
使用場景: 一、須要生成的對象具備複雜的內部結構。 二、須要生成的對象內部屬性自己相互依賴。
注意事項:與工廠模式的區別是:建造者模式更加關注與零件裝配的順序。
角色介紹:
通用類型代碼以下:
(1)產品類
產品類的方法和屬性都是爲了添加產品自身的屬性,有些產品須要有多個屬性,有些產品可能只有一個屬性。下面的代碼只是一個例子,其餘屬性以此方式添加便可。
/** * 產品類 */ public class Product { protected String name; public void setName(String name) { this.name = name; } }
(2)抽象建造者類
與產品的各個屬性相對應,併爲之提供相應的方法。這個就像靜態代理模式,經過代理類 Builder 來生成被代理對象 Product。
/** * 抽象建造者 * 若是有多個產品類就有幾個具體的建造者,並且這多個產品類具備相同接口或抽象類 */ public abstract class Builder { // 跟產品中的具體方法相對應 public abstract void setName(String name); // 建造產品 public abstract Product create(); }
(3)具體建造者類
每一個具體的 builder 都有本身獨特的屬性,都是對某一種具體 Product 的代理。
public class ConcreteBuilder extends Builder { private Product product = new Product();
// 設置具體的產品名字 @Override public void setName(String name) { product.setName(name); } @Override public Product create() { return product; } }
(4)指揮者(導演)類
經過 Director 來生成具體的 Product。但其實也能夠直接採用鏈式的方式生成,每一個 ConcreteBuilder 的方法返回 this 便可,最後在調用 create 方法,即建立成功。這樣就能夠省去 Director 這個類,邏輯也更加清晰。
/** * 指揮者類 * 指揮者類起到封裝的做用,避免高層模塊深刻到建造者內部的實現類 */ public class Director { // 構建不一樣的產品 private Builder mBuilder = null; // 根據須要傳入具體的產品 public Director(Builder builder) { mBuilder = builder; } // 獲取最終的產品 public Product getAProduct(String name) { mBuilder.setName(name); // 設置不一樣的零件,產生不一樣的產品 return mBuilder.create(); } }
在《 Effective Java 第2版 》中有提到,遇到多個構造器參數時要考慮使用構建器(Builder模式)。相比於重疊構造器(telescoping constructor)模式和 JavaBeans 模式,Builder 模式實現的對象更利於使用。
在這種模式下,你提供第一個只有必要參數的構造器,第二個構造器有一個可選參數,第三個有兩個可選參數,依此類推,最後一個構造器包含全部的可選參數。下面看看其編程實現:
/** * 使用重疊構造器模式 */ public class Person { //必要參數 private final int id; private final String name; //可選參數 private final int age; private final String sex; private final String phone; private final String address; private final String desc; public Person(int id, String name) { this(id, name, 0); } public Person(int id, String name, int age) { this(id, name, age, ""); } public Person(int id, String name, int age, String sex) { this(id, name, age, sex, ""); } public Person(int id, String name, int age, String sex, String phone) { this(id, name, age, sex, phone, ""); } public Person(int id, String name, int age, String sex, String phone, String address) { this(id, name, age, sex, phone, address, ""); } public Person(int id, String name, int age, String sex, String phone, String address, String desc) { this.id = id; this.name = name; this.age = age; this.sex = sex; this.phone = phone; this.address = address; this.desc = desc; } }
從上面的代碼中,當你想要建立實例的時候,就利用參數列表最短的構造器,但該列表中包含了要設置的全部參數:
Person person = new Persion(1, "李四", 20, "男", "18800000000", "China", "測試使用重疊構造器模式");
這個構造器調用一般須要許多你本不想設置的參數,但仍是不得不爲它們傳遞值。
一句話:重疊構造器可行,可是當有許多參數的時候,建立使用代碼會很難寫,而且較難以閱讀。
遇到許多構造器參數的時候,還有第二種代替辦法,即 JavaBeans 模式。在這種模式下,調用一個無參構造器來建立對象,而後調用 setter 方法來設置每一個必要的參數,以及每一個相關的可選參數:
/** * 使用JavaBeans模式 */ public class Person { //必要參數 private int id; private String name; //可選參數 private int age; private String sex; private String phone; private String address; private String desc; public void setId(int id) { this.id = id; } public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } public void setSex(String sex) { this.sex = sex; } public void setPhone(String phone) { this.phone = phone; } public void setAddress(String address) { this.address = address; } public void setDesc(String desc) { this.desc = desc; } }
建立各個須要的對象:
Person p1=new Person(); Person p2=new Person("張三"); Person p3=new Person("李四",18); Person p4=new Person("王五",21,180); Person p5=new Person("趙六",17,170,65.4);
能夠想象一下這樣建立的壞處,最直觀的就是四個參數的構造函數的最後面的兩個參數究竟是什麼意思,可讀性不怎麼好,若是不點擊看源碼,不知道哪一個是 weight 哪一個是 height。還有一個問題就是當有不少參數時,編寫這個構造函數就會顯得異常麻煩,這時候若是換一個角度,試試 Builder 模式,你會發現代碼的可讀性一會兒就上去了。
經過與其餘兩種模式的對比,才能更加清楚地知道建造者模式 (Builder) 到底好在什麼地方,爲何這樣設計。
參考文獻
一、建造者模式