設計模式——建造者模式
建造者模式(Builder Pattern):使用簡單的物件一步一步構建成一個複雜的物件。這種設計模式屬於建立者模式,它提供了一種建立物件的最佳方式。一個 Builder 類會一步一步構造最終的物件。該 Builder 類是獨立於其他物件的。例如,計算機是由 CPU、主機板、記憶體、硬碟、顯示卡、機箱、顯示器、鍵盤、滑鼠等部件組裝而成的,採購員不可能自己去組裝計算機,而是將計算機的配置要求告訴計算機銷售公司,計算機銷售公司安排技術人員去組裝計算機,然後再交給要買計算機的採購員。
以上所有這些產品都是由多個部件構成的,各個部件可以靈活選擇,但其建立步驟都大同小異。這類產品的建立無法用前面介紹的工廠模式描述,只有建造者模式可以很好地描述該類產品的建立。
一、基本介紹
1)、建造者模式:又叫生成器模式,是一種物件構建模式。它可以將複雜物件的建造過程抽象出來(抽象類別),使這個抽象過程的不同實現方法可以構造出不同表現(屬性)的物件。
2)、構造者模式,它允許使用者只通過指定複雜物件的型別和內容就可以構建它們,使用者不需要知道內部的具體構建細節。
3)、構造者模式主要解決在軟體系統中,當面臨一個複雜物件的建立工作時,其通常由各個部分的子物件用一定的演算法構成;由於需求的變化,這個複雜物件的各個部分經常面臨劇烈的變化,但是將它們組合在一起的演算法卻相對穩定。
4)、主要適用於:一些基本部件不會變,而其組合經常變化的情況。主要是將變與不變進行分離(去肯德基,漢堡、可樂、薯條、炸雞翅等是不變的,而其組合是經常變化的,生成出所謂的"套餐")。
二、建造者模式的四個角色
1)、Product(產品角色):包含多個組成部件的複雜物件(產品物件)。
2)、Builder(抽象建造者):建立一個包含 Product 各個子部件的抽象方法或介面,通常還包含了一個返回複雜產品的方法。
3)、ConcreteBuilder(具體建造者):實現 Builder 介面,構建和裝備各個子部件的具體實現。
4)、Director(指揮者):構建一個使用 Builder 介面的物件。它主要作用是用於建立一個複雜的物件。在指揮者中不涉及具體產品的資訊。主要分兩個作用,一是隔離客戶與物件的生產過程,二是負責控制物件的生產過程。
三、建造者模式的注意事項和細節
1)、客戶端不必知道產品內部組成的細節,將產品本身與產品的建立過程解耦,使得相同的建立過程可以建立不同的產品物件。
2)、每一個具體建造者都相對獨立,因此可以方便的替換或者新增具體建造者,使用者使用不同的具體建造者即可得到不同的產品物件。
3)、可以更加精準的控制產品的建立過程。將複雜產品的建立步驟分解在不同的方法中,使得建立過程更加清晰,也更方便使用程式來控制建立過程。
4)、增加新的具體建築者無需修改原有類庫程式碼,指揮者類針對抽象建造者類程式設計,系統擴充套件方便。符合 OCP 原則。
5)、建造者模式建立的產品具有較多的共同點,其組成部分相似,如果產品之間的差異性很大,則不適合建造者模式,因此其使用範圍受到一定的限制。
6)、如果產品的內部變化複雜,可能會導致需要定義很多具體建造者內來實現這種變化,導致系統變得龐大,因此這種情況下,考慮是否選擇建造者模式。
7)、抽象工廠模式 VS 建造者模式:抽象工廠模式實現對產品家族的建立,一個產品家族就是一系列產品。具有不同分類維度的產品組合,採用抽象工廠模式不需要關係構建過程,只關係產品由什麼工廠生成即可。而建造者模式則是要求按照指定的藍圖建造產品,它主要目的是通過組裝零配件而產生一個新產品。
四、建造者模式原理類圖:可以參考五中的程式碼一起觀看
五、建造者模式案例
【1】建立 Product 產品角色:
1 public class Product { 2 private String CPU; 3 //主機板 mainboard 4 private String mainboard; 5 //記憶體 memory 6 private String memory; 7 //.....略 8 //生成get set 方法 略 9 }
【2】抽象建造者物件:
1 public abstract class Builder { 2 //組合 產品物件 3 protected Product product = new Product(); 4 5 //抽象方法 6 public abstract void buildCpu(); 7 public abstract void buildMainboard(); 8 public abstract void buildMemory(); 9 10 //返回一個產品物件 11 public Product getResult() { 12 return product; 13 } 14 }
【3】具體建造者物件:一般會有多個這種物件,都實現 Builder 抽象類。
1 public class ConcreteBuilder_huawei extends Builder{ 2 3 @Override 4 public void buildCpu() { 5 product.setCPU("華為CPU"); 6 } 7 8 @Override 9 public void buildMainboard() { 10 product.setMainboard("華為主機板"); 11 } 12 13 @Override 14 public void buildMemory() { 15 product.setMemory("華為記憶體"); 16 } 17 18 }
【4】指揮者類,主要構建組裝的流程:返回的是產品物件
1 public class Director { 2 //需要將抽象類聚合進來 3 Builder builder = null; 4 //建立一個構造器 呼叫時傳入具體的實現類 5 public Director(Builder builder) { 6 this.builder = builder; 7 } 8 //新增set 方法 使用者可以通過此方法修改已有的 建造者物件 9 public void setBuilder(Builder builder) { 10 this.builder = builder; 11 } 12 13 //筆記本的組成流程 返回的是產品類 14 public Product pack_pc() { 15 builder.buildMainboard(); 16 builder.buildCpu(); 17 builder.buildMemory(); 18 return builder.getResult(); 19 } 20 }
【5】客戶端通過指揮者類呼叫需要的品牌電腦:
1 public class Client { 2 public static void main(String[] args) { 3 //建立需要的電腦子部件——例如我們要組裝的是 華為 4 ConcreteBuilder_huawei huawei = new ConcreteBuilder_huawei(); 5 //呼叫指揮者類,將需要的品牌傳入,根據控制著中的流程進行組裝 6 Director director = new Director(huawei); 7 //呼叫組裝方法,返回產品 8 Product pack_pc = director.pack_pc(); 9 //檢視輸入的結果 10 System.out.println(pack_pc.toString()); 11 //結果為:Product [CPU=華為CPU, mainboard=華為主機板, memory=華為記憶體] 12 } 13 }
六、原始碼分析(StringBuilder)
【1】java.lang.StringBuilder 中的建造者模式:
1 public class OrginBuilder { 2 public static void main(String[] args) { 3 StringBuilder builder = new StringBuilder("hello"); 4 builder.append("world"); 5 } 6 }
【2】進入 append 方法,會發現 StringBuilder 類即就是我們所說的指揮者。建造方法的具體實現是由 AbstractStringBuilder 實現。
1 @Override 2 public StringBuilder append(String str) { 3 super.append(str); 4 return this; 5 }
【3】進入 AbstractStringBuilder 類,實現了 Appendable 介面方法,此類已是建造者,只是不能例項化。
1 public AbstractStringBuilder append(String str) { 2 if (str == null) 3 return appendNull(); 4 int len = str.length(); 5 ensureCapacityInternal(count + len); 6 str.getChars(0, len, value, count); 7 count += len; 8 return this; 9 }
【4】Appendable 介面定義了多個 append 方法(抽象方法),既 Appendle 為抽象建造者,定義了抽象方法。
1 public interface Appendable { 2 3 Appendable append(CharSequence csq) throws IOException; 4 ...... 5 }