設計模式——建造者模式
引言
之前在家看兩個小外甥玩軌道車,拆開包裝,一堆小零件,兄弟兩一個拼橋樑、彎道、路標,一個裝車、搭立交、組裝上下坡。不一會兒軌道就全拼好了,兩兄弟用代表自己的車子在軌道上追逐,玩的很開心。我看了下軌道車包裝,根據使用零件多少不同,組拼順序不同,擺放不同可以建立不同的軌道和街道,有橢圓形的,上下立交式的,單層的……
忽然想到用程式來表述玩軌道車的流程,如果用類圖描述軌道車的玩法,可以簡單表示為:
分為兩個部分:
紅色為軌道車的各個部件,Road規定了軌道車可以有 坡度、彎道、橋樑、路標、汽車、立交這幾個部分;
藍色部分為建造不同的軌道,環形軌道(AnnularBuilder)和立交軌道(InterchangeBuilder)。
Road為模板方法的變形形式,軌定軌道車的部件和各個部件的組裝順序,實現如下:
public abstract class Road { //坡度 protected abstract void slope(); //彎道 protected abstract void curve(); //橋樑 protected abstract void bridge(); //路標 protected abstract void guide(); //汽車 protected abstract void car(); //立交 protected abstract void interchange(); private List<String> list = new ArrayList<>(); final public void create() throws InvocationTargetException, IllegalAccessException { Method[] methods = this.getClass().getDeclaredMethods(); for (String method : list) { for (int i = 0; i < methods.length; i++) { if (methods[i].getName().equals(method)) { methods[i].invoke(this, null); } } } } final public void setList(List<String> methods){ this.list = methods; } }
組裝軌道車的部件實現方法:
public class TrackRoad extends Road { //坡度 @Override protected void slope() { System.out.println("建造上下坡……"); } //彎道 @Override protected void curve() { System.out.println("建造麴線車道……"); } //橋樑 @Override protected void bridge() { System.out.println("建造橋樑……"); } //路標 @Override protected void guide() { System.out.println("放置公路路標……"); } //汽車 @Override protected void car() { System.out.println("建造汽車……"); } //立交 @Override protected void interchange() { System.out.println("建造立交……"); } }
抽象建立軌道車類Builder定義了軌道車的組裝部件和獲取組裝的軌道車
public abstract class Builder { /** * 不同部件的建立 */ public abstract void setPart(List<String> methods); /** * 建造軌道 */ public abstract TrackRoad buildRoad() throws InvocationTargetException, IllegalAccessException; }
具體軌道車玩法建造類(立交軌道車):
public class InterchangeBuilder extends Builder { private TrackRoad road = new TrackRoad(); @Override public void setPart(List<String> methods) { this.road.setList(methods); } @Override public TrackRoad buildRoad() { return this.road; } }
如果想建立一個立交軌道,可以這麼建立:
InterchangeBuilder interchangeBuilder = new InterchangeBuilder(); List<String> interMethods = new ArrayList<>(); interMethods.add("slope"); interMethods.add("bridge"); interMethods.add("guide"); interMethods.add("interchange"); interMethods.add("car"); interchangeBuilder.setPart(interMethods); interchangeBuilder.buildRoad().create();
建造上下坡……
建造橋樑……
放置公路路標……
建造立交……
建造汽車……
引入導演類
坡度、彎道、橋樑、路標、汽車、立交不同組裝方式可以構造不同的軌道,為了方便 支援的很多不同的軌道,可以增加個導演類(Director)。
導演類封裝立交軌道和環形軌道的實現,對外提供直接獲取的方法:
public class Director { private List<String> steps = new ArrayList<>(); private InterchangeBuilder interchangeBuilder = new InterchangeBuilder(); private AnnularBuilder annularBuilder = new AnnularBuilder(); public TrackRoad getInterchangeBuilder(){ System.out.println("===========================建造立交車道==========================="); this.steps.clear(); this.steps.add("slope"); this.steps.add("bridge"); this.steps.add("guide"); this.steps.add("interchange"); this.steps.add("car"); this.interchangeBuilder.setPart(this.steps); return this.interchangeBuilder.buildRoad(); } public TrackRoad getAnnularBuilder(){ System.out.println("===========================建造麴線車道==========================="); this.steps.clear(); this.steps.add("curve"); this.steps.add("bridge"); this.steps.add("guide"); this.steps.add("car"); this.annularBuilder.setPart(this.steps); return this.annularBuilder.buildRoad(); } }
客戶端不關注如何實現,只需要拿來即用(可玩):
public void testDerictor() throws InvocationTargetException, IllegalAccessException { Director director = new Director(); director.getAnnularBuilder().create(); director.getInterchangeBuilder().create(); }
其實這就是建造者模式,由導演類決定如何構建具體的物件(產品)。
建造者模式
定義
將一個複雜物件的構建與它的表示分離,使得同樣的構建過程可以建立不同的表示。
通用類圖
四要素
建造者模式中有以下4個角色:
Product產品類
通常是實現模板方法模式(有模板方法和基本方法的類),例子裡的 TrackRoad(軌道) 就屬於產品類。
Builder抽象建造者
規範產品的組建,一般是抽象類,約定功能由子類去實現具體的建造方法,對應例子裡的Builder。
ConcreteBuilder具體建造者
實現抽象建造者的所有方法。例子裡的InterchangeBuilder和AnnularBuilder就是具體軌道的建造者。
Director導演類
封裝具體建造者,提供簡單易用的構建產品類方法。
總結
優點
封裝性:將產品本身與產品建立過程進行解耦,可以使用相同的建立過程來得到不同的產品。也就說細節依賴抽象。
建造者獨立,容易擴充套件:增加新的具體建造者無需修改原有類庫的程式碼,易於拓展,符合“開閉原則“。
缺點
建造者模式所建立的產品一般具有較多的共同點,其組成部分相似;如果產品之間的差異性很大,則不適合使用建造者模式,因此其使用範圍受到一定的限制。
使用場景
- 相同的方法,不同的執行順序,產生不同的事件結果時
- 多個部件或零件都可以裝配到一個物件中,但是產生的執行結果又不相同時
- 產品類非常複雜,或者產品類中的呼叫順序不同產生了不同的效能