建造者模式是較爲複雜的建立型模式,它將客戶端與包含多個組成部分(或部件)的複雜對象的建立過程分離,客戶端無須知道複雜對象的內部組成部分與裝配方式,只須要知道所需的建造者類型便可。建造者模式關注一步一步地建立一個複雜對象,不一樣的具體建造者定義了不一樣的建立過程,且具體建造者相互獨立,增長新的建造者很是方便,無須修改已有代碼,系統具備較好的擴展性。java
建造者模式中用到了複雜對象這個概念。編程
複雜對象就是指那些包含多個成員變量的對象,這些成員變量也叫部件或者零件,例如汽車包括方向盤,發動機,輪胎等 ,
汽車就是複雜對象,方向盤,發動機以及輪胎就是汽車的部件。微信
建造者模式:將一個複雜的對象的構建與它的表示分離,使得一樣的構建過程能夠建立不一樣的表示。ide
建造者模式是一種對象建立型模式。函數
建造者模式包含如下四個角色:post
Builder
(抽象建造者):爲建立一個產品Product對象的各個部件指定抽象接口,在該接口中通常聲明兩類方法,一類是buildXXX()
方法,用於建立複雜對象的各個部分(部件),另外一類是是getResult()
,用於返回複雜對象。Builder
既能夠是抽象類,也能夠是接口ConcreteBuilder
(具體建造者):實現了Builder
接口或者繼承了Builder
類,實現各個部件的具體構造和裝配方法,定義並明確其所建立的複雜對象,也能夠提供一個方法返回建立好的複雜對象Product
(產品角色):是被構建的複雜對象,包含多個組成部件,具體建造者建立該產品的內部表示並定義其裝配過程Director
(指揮者):指揮者又叫導演類,複雜安排複雜對象的建造次序,指揮者與抽象建造者之間存在關聯關係,能夠在其construct()
建造方法中調用建造者對象的部件構造以及裝配方法,完成複雜對象的建造。客戶端通常只須要與導演類進行交互,在客戶端肯定具體建造者的類型,並實例化具體建造者對象,而後經過導演類的構造函數或者setter將該對象傳入導演類中bulidXXX
的數量,同時聲明相似getResult
返回產品角色對象的方法buildXXX
方法construct
的方法給外界,調用具體建造者的方法並返回產品角色對象通常來講Product
是一個複雜對象,典型的實現以下:測試
class Product { private type1 part1; private type2 part2; private type3 part3; //getter + setter ... }
其中type1
,type2
等指各類不一樣的類型,通常來講會有嵌套類。優化
抽象建造者的典型實現以下:ui
abstract class Builder { protected Product product = new Product(); public abstract void buildPart1(); public abstract void buildPart2(); public abstract void buildPart3(); public Product getResult() { return product; } }
抽象建造者中聲明瞭一系列buildXXX
方法,用於建立Product
的各個部件,具體建立過程在ConcreteBuilder
中實現,getResult()
返回已建立完成的Product
。this
ConcreteBuilder
實現了Builder
中的buildXXX
方法,經過調用Product
的setter來實現給產品對象的各部分賦值。
不一樣的ConcreteBuilder
在實現buildXXX
時將有所區別,好比傳入Product
的setter參數的不一樣。
另外在有些ConcreteBuilder
中某些buildXXX
無須實現(提供一個空實現),這些對客戶端來講無須關心,客戶端只須要知道具體建造者的類型便可。
典型實現以下:
class ConcreteBuilder extends Builder { public void buildPart1() { product.setPart1("part1"); } public void buildPart2() { product.setPart2("part2"); } public void buildPart3() { product.setPart3("part3"); } }
Director
類主要有兩個做用:
buildXXX
方法是否被調用,以及調用時的前後次序等等指揮者針對抽象建造者編程,客戶端只須要知道具體建造者的類型,便可經過指揮者調用建造者的相關方法,返回一個完整的產品對象。典型實現以下:
class Director { private Builder builder; public Director(Builder builder) { this.builder = builder; } public void setBuilder(Builder builder) { this.builder = builder; } public Product construct() { builder.buildPart1(); builder.buildPart2(); builder.buildPart3(); return builder.getResult(); } }
建立具體建造者並傳入導演類做爲構造方法參數,而後調用construct
便可獲取產品對象:
public static void main(String[] args) { Director director = new Director(new ConcreteBuilder()); Product product = director.construct(); System.out.println(product.getPart1()); }
遊戲角色的建立:不一樣的角色具備差異極大的外部特徵,並且要求隨着遊戲的進行會不斷出現新的角色,也就是說擴展性要好。這裏例子簡化就建立三個角色:英雄,天使與惡魔,使用建造者模式進行設計。
設計以下:
Actor
ActorBuilder
HeroBuilder
+AngelBuilder
+DevilBuilder
ActorController
代碼以下:
// 複雜產品 class Actor { private String type; private String face; private String costume; private String hairstyle; //getter and setter ... } //抽象建造者 abstract class ActorBuilder { protected Actor actor = new Actor(); public abstract void buildType(); public abstract void buildFace(); public abstract void buildCostume(); public abstract void buildHairstyle(); public Actor createActor() { return actor; } } //具體建造者 class HeroBuilder extends ActorBuilder { public void buildType(){ actor.setType("英雄"); } public void buildFace(){ actor.setFace("英俊"); } public void buildCostume(){ actor.setCostume("盔甲"); } public void buildHairstyle(){ actor.setHairstyle("飄逸"); } } class AngleBuilder extends ActorBuilder { public void buildType(){ actor.setType("天使"); } public void buildFace(){ actor.setFace("漂亮"); } public void buildCostume(){ actor.setCostume("白裙"); } public void buildHairstyle(){ actor.setHairstyle("披肩長髮"); } } class DevilBuilder extends ActorBuilder { public void buildType(){ actor.setType("惡魔"); } public void buildFace(){ actor.setFace("帥氣"); } public void buildCostume(){ actor.setCostume("黑衣"); } public void buildHairstyle(){ actor.setHairstyle("紅色"); } } // 指揮者類 class ActorController { public Actor construct(ActorBuilder builder) { builder.buildType(); builder.buildFace(); builder.buildHairstyle(); builder.buildCostume(); return builder.createActor(); } }
測試類:
public class Test { public static void main(String[] args) { ActorBuilder builder = new AngleBuilder(); ActorController controller = new ActorController(); Actor actor = controller.construct(builder); System.out.println(actor.getType()); System.out.println(actor.getCostume()); System.out.println(actor.getHairstyle()); System.out.println(actor.getFace()); } }
Director
其實Director
是能夠省略的,直接與Builder
合併,在Builder
中提供相似Direcotr
中的construct()
方法,並定義爲靜態方法,如:
abstract class ActorBuilder { protected static Actor actor = new Actor(); public abstract void buildType(); public abstract void buildFace(); public abstract void buildCostume(); public abstract void buildHairstyle(); public static Actor build(ActorBuilder builder) { builder.buildType(); builder.buildFace(); builder.buildHairstyle(); builder.buildCostume(); return actor; } }
同時客戶端代碼修改以下:
Actor actor = ActorBuilder.build(new AngleBuilder()); //Actor actor = ActorBuilder.build(new HeroBuilder()); //Actor actor = ActorBuilder.build(new DevilBuilder());
再簡單一點的能夠省略createActor
中的參數:
abstract class ActorBuilder { protected Actor actor = new Actor(); public abstract void buildType(); public abstract void buildFace(); public abstract void buildCostume(); public abstract void buildHairstyle(); public Actor build() { buildType(); buildFace(); buildHairstyle(); buildCostume(); return actor; } }
同時客戶端簡化以下:
Actor actor = new AngleBuilder().build();
這兩種方式簡化了系統結構的同時又不影響靈活性以及可擴展性,可是加劇了抽象建造者的職責,若是build
方法較爲複雜,待構建的產品組成部分較多,建議仍是將其單獨封裝在Director
中,這樣更加符合SRP(單一權責原則)。
鉤子方法是一種能夠控制是否調用某個buildXXX
的方法,特徵以下:
boolean
isXXX
例如修改ActorBuilder
以下:
abstract class ActorBuilder { protected Actor actor = new Actor(); public abstract void buildType(); public abstract void buildFace(); public abstract void buildCostume(); public abstract void buildHairstyle(); public boolean isBareheaded() { return false; } public Actor createActor() { return actor; } }
並修改DevilBuilder
,覆蓋默認方法:
class DevilBuilder extends ActorBuilder { public void buildType(){ actor.setType("惡魔"); } public void buildFace(){ actor.setFace("帥氣"); } public void buildCostume(){ actor.setCostume("黑衣"); } public void buildHairstyle(){ actor.setHairstyle("紅色"); } public boolean isBareheaded(){ return true; } }
最後修改ActorController
:
class ActorController { public Actor construct(ActorBuilder builder) { builder.buildType(); builder.buildFace(); builder.buildCostume(); if(builder.isBareheaded()) builder.buildHairstyle(); return builder.createActor(); } }
相比起以前的ActorController
多了一次判斷,測試以下:
public static void main(String[] args) { ActorController controller = new ActorController(); Actor actor = controller.construct(new AngleBuilder()); System.out.println(actor.getType()); System.out.println(actor.getCostume()); System.out.println(actor.getHairstyle()); System.out.println(actor.getFace()); System.out.println(); actor = controller.construct(new DevilBuilder()); System.out.println(actor.getType()); System.out.println(actor.getCostume()); System.out.println(actor.getHairstyle()); System.out.println(actor.getFace()); }
輸出以下:
在實際應用中Director
較少出現,一般只有Builder
以及Product
,並且Builder
是做爲Product
的內部類,提供一系列set
方法,這些set
方法返回一個Builder
方便後續調用,最後以一個build()
結尾,好比OkHttp中的Request
/OkHttpClient
:
OkHttpClient client = new OkHttpClient.Builder() .connectTimeout(5000,TimeUnit.MILLISECONDS) .readTimeout(10,TimeUnit.SECONDS) .build(); Request request = new Request.Builder() .url("https://xxx") .post(requestBody) .build();
若是以爲文章好看,歡迎點贊。
同時歡迎關注微信公衆號:氷泠之路。