30.2 抽象工廠模式VS建造者模式
抽象工廠模式實現對產品家族的創建,一個產品家族是這樣的一系列產品:具有不同分類維度的產品組合,采用抽象工廠模式則是不需要關心構建過程,只關心什么產品由什么工廠生產即可。而建造者模式則是要求按照指定的藍圖建造產品,它的主要目的是通過組裝零配件而產生一個新產品,兩者的區別還是比較明顯的,但是還有讀者對這兩個模式產生混淆,我們通過一個例子說明兩者的差別。
現代化的汽車工廠能夠批量生產汽車(不考慮手工打造的豪華車)。不同的工廠生產不同的汽車,寶馬工廠生產寶馬牌子的車,奔馳工廠生產奔馳牌子的車。車不僅具有不同品牌,還有不同的用途分類,如商務車Van,運動型車SUV等,我們按照兩種設計模式分別實現車輛的生產過程。
30.2.1 按抽象工廠模式生產車輛
按照抽象工廠模式,首先需要定義一個抽象的產品接口即汽車接口,然后寶馬和奔馳分別實現該接口,由于它們只具有了一個品牌屬性,還沒有定義一個具體的型號,屬于對象的抽象層次,每個具體車型由其子類實現,如R系列的奔馳車是商務車,X系列的寶馬車屬于SUV,我們來看類圖,如圖30-3所示。

圖30-3 車輛生產的工廠類圖
在類圖中,產品類很簡單,我們從兩個維度看產品:品牌和車型,每個品牌下都有兩個車型,如寶馬SUV,寶馬商務車等,同時我們又建造了兩個工廠,一個專門生產寶馬車的寶馬工廠BMWFactory,一個是生產奔馳車的奔馳車生產工廠BenzFactory。當然,汽車工廠也有兩個不同的維度,可以建立這樣兩個工廠:一個專門生產SUV車輛的生產工廠,生產寶馬SUV和奔馳SUV,另外一個工廠專門生成商務車,分別是寶馬商務車和奔馳商務車,這樣設計在技術上是完全可行的,但是在業務上是不可行的,為什么?這是因為你看到過有一個工廠既能生產奔馳SUV也能生產寶馬SUV嗎?這是不可能的,因為業務受限,除非是國內的山寨工廠。我們先來看產品類,汽車接口如代碼清單30-12所示。
代碼清單30-12 汽車接口
public?interface?ICar?{
?????//汽車的生產商,也就是牌子
?????public?String?getBand();
?????//汽車的型號
?????public?String?getModel();
}
在產品接口中我們定義了車輛有兩個可以查詢的屬性:品牌和型號,奔馳車和寶馬車是兩個不同品牌的產品,但不夠具體,只是知道它們的品牌而已,還不能夠實例化,因此還是一個抽象類,如代碼清單30-13所示。
代碼清單30-13 抽象寶馬車
public?abstract?class?AbsBMW?implements?ICar?{
?????private?final?static?String?BMW_BAND?=?"寶馬汽車";
?????//寶馬車
?????public?String?getBand()?{
?????????????return?BMW_BAND;
?????}
?????//型號由具體的實現類實現
?????public?abstract?String?getModel();
}
抽象產品類中實現了產品的類型定義,車輛的型號沒有實現,兩實現類分別實現商務車和運動型車,分別如代碼清單30-14、代碼清單30-15所示。
代碼清單30-14 寶馬商務車
public?class?BMWVan?extends?AbsBMW?{
?????private?final?static?String?SEVENT_SEARIES?=?"7系列車型商務車";
?????public?String?getModel()?{
?????????????return?SEVENT_SEARIES;
?????}
}
代碼清單30-15 寶馬SUV
public?class?BMWSuv?extends?AbsBMW?{
?????private?final?static?String?X_SEARIES?=?"X系列車型SUV";
?????public?String?getModel()?{
?????????????return?X_SEARIES;
?????}
}
奔馳車與寶馬車類似,都已經有清晰品牌定義,但是型號還沒有確認,也是一個抽象的產品類,如代碼清單30-16所示。
代碼清單30-16 抽象奔馳車
public?abstract?class?AbsBenz?implements?ICar?{
?????private?final?static?String?BENZ_BAND?=?"奔馳汽車";
?????public?String?getBand()?{
?????????????return?BENZ_BAND;
?????}
?????//具體型號由實現類完成
?????public?abstract?String?getModel();
}
由于分類的標準是相同的,因此奔馳車也應該有商務車和運動車兩個類型,分別如代碼清單30-17和代碼清單30-18所示。
代碼清單30-17 奔馳商務車
public?class?BenzVan?extends?AbsBenz?{
?????private?final?static?String?R_SERIES?=?"R系列商務車";
?????public?String?getModel()?{
?????????????return?R_SERIES;
?????}
}
代碼清單30-18???奔馳SUV
public?class?BenzSuv?extends?AbsBenz?{
?????private?final?static?String?G_SERIES?=?"G系列SUV";
?????public?String?getModel()?{
?????????????return?G_SERIES;
?????}
}
所有的產品類都已經實現了,剩下的工作就是要定義工廠類進行生產,由于產品類型多樣,也導致了必須有多個工廠類來生產不同產品,首先就需要定義一個抽象工廠,聲明每個工廠必須完成的職責,如代碼清單30-19所示。
代碼清單30-19 抽象工廠
public?interface?CarFactory?{
?????//生產SUV
?????public?ICar?createSuv();
?????//生產商務車
?????public?ICar?createVan();
}
抽象工廠定義了每個工廠必須生產兩個類型車:SUV(運動車)和VAN(商務車),否則一個工廠就不能被實例化,我們來看寶馬車工廠,如代碼清單30-20所示。
代碼清單30-20 寶馬車工廠
public?class?BMWFactory?implements?CarFactory?{
?????//生產SUV
?????public?ICar?createSuv()?{
?????????????return?new?BMWSuv();
?????}
?????//生產商務車
?????public?ICar?createVan(){
?????????????return?new?BMWVan();
?????}
}
很簡單,你要我生產寶馬商務車,沒問題,直接產生一個寶馬商務車對象,返回給調用者,這對調用者來說根本不需要關心到底是怎么生產的,它只要找到一個寶馬工廠,即可生產出自己需要的產品(汽車)。奔馳車工廠與此類似,如代碼清單30-21所示。
代碼清單30-21 奔馳車工廠
public?class?BenzFactory?implements?CarFactory?{
?????//生產SUV
?????public?ICar?createSuv()?{
?????????????return?new?BenzSuv();
?????}
?????//生產商務車
?????public?ICar?createVan(){
?????????????return?new?BenzVan();
?????}
}
產品和工廠都具備了,剩下的工作就是建立一個場景類模擬調用者調用,如代碼清單30-22所示。
代碼清單30-22 場景類
public?class?Client?{
?????public?static?void?main(String[]?args)?{
?????????????//要求生產一輛奔馳SUV
?????????????System.out.println("===要求生產一輛奔馳SUV===");
?????????????//首先找到生產奔馳車的工廠
?????????????System.out.println("A、找到奔馳車工廠");
?????????????CarFactory?carFactory=?new?BenzFactory();
?????????????//開始生產奔馳SUV
?????????????System.out.println("B、開始生產奔馳SUV");
?????????????ICar?benzSuv?=?carFactory.createSuv();
?????????????//生產完畢,展示一下車輛信息
?????????????System.out.println("C、生產出的汽車如下:");
?????????????System.out.println("汽車品牌:"+benzSuv.getBand());
?????????????System.out.println("汽車型號:"?+?benzSuv.getModel());
?????}
}
運行結果如下所示:
===要求生產一輛奔馳SUV===
A、找到奔馳車工廠
B、開始生產奔馳SUV
C、生產出的汽車如下:
汽車品牌:奔馳汽車
汽車型號:G系列SUV
對外界調用者來說,只要更換一個具備相同結構的對象,即可發生非常大的改變,如我們原本使用BenzFactory生產汽車,但是過了一段時間后,我們的系統需要生產寶馬汽車,這對系統來說不需要很大的改動,只要把工廠類使用BMWFactory代替即可,立刻可以生產出寶馬車,注意這里生產的是一輛完整的車,對于一個產品,只要給出產品代碼(車類型)即可生產,抽象工廠模式把一輛車認為是一個完整的、不可拆分的對象。它注重完整性,一個產品一旦找到一個工廠生產,那就是固定的型號,不會出現一個寶馬工廠生產奔馳車的情況。那現在的問題是我們就想要一輛混合的車型,如奔馳的引擎,寶馬的車輪,那該怎么處理呢?使用我們的建造者模式!
30.2.2 按建造者模式生產車輛
按照建造者模式設計一個生產車輛需要把車輛進行拆分,拆分成引擎和車輪兩部分,然后由建造者進行建造,想要什么車,你只要有設計圖紙就成,馬上可以制造一輛車出來。它注重的是對零件的裝配、組合、封裝,它從一個細微構件裝配角度看待一個對象。我們來看生產車輛的類圖,如圖30-4所示。
注意看我們類圖中的藍圖類Blueprint,它負責對產品建造過程定義。既然要生產產品,那必然要對產品進行一個描述,在類圖中我們定義了一個接口來描述汽車,如代碼清單30-23所示。
代碼清單30-23 車輛產品描述
public?interface?ICar?{
?????//汽車車輪
?????public?String?getWheel();
?????//汽車引擎
?????public?String?getEngine();
}

圖30-4 建造者模式建造車輛
我們定義一輛車必須有車輪和引擎,具體的產品如代碼清單30-24所示。
代碼清單30-24 具體車輛
public?class?Car?implements?ICar?{
?????//汽車引擎
?????private?String?engine;
?????//汽車車輪
?????private?String?wheel;
?????//一次性傳遞汽車需要的信息
?????public?Car(String?_engine,String?_wheel){
?????????????this.engine?=?_engine;
?????????????this.wheel?=?_wheel;
?????}
?????public?String?getEngine()?{
?????????????return?engine;
?????}
?????public?String?getWheel()?{
?????????????return?wheel;
?????}
?????public?String?toString(){
?????????????return?"車的輪子是:"?+?wheel?+?"\n車的引擎是:"?+?engine;
?????}
}
一個簡單的JavaBean定義產品的屬性,明確對產品的描述。我們繼續來思考,因為我們的產品是比較抽象的,它沒有指定引擎的型號,也沒有指定車輪的牌子,那么這樣的組合方式有很多,完全要靠建造者來建造,建造者說要生產一輛奔馳SUV那就得用奔馳的引擎和奔馳的車輪,該建造者對于一個具體的產品來說是絕對的權威,我們來描述一下建造者,如代碼清單30-25所示。
代碼清單30-25 抽象建造者
public?abstract?class?CarBuilder?{
?????//待建造的汽車
?????private?ICar?car;
?????//設計藍圖
?????private?Blueprint?bp;
?????public?Car?buildCar(){
?????????????//按照順序生產一輛車
?????????????return?new?Car(buildEngine(),buildWheel());
?????}
?????//接收一份設計藍圖
?????public?void?receiveBlueprint(Blueprint?_bp){
?????????????this.bp?=?_bp;
?????}
?????//查看藍圖,只有真正的建造者才可以查看藍圖
?????protected?Blueprint?getBlueprint(){
?????????????return?bp;
?????}
?????//建造車輪
?????protected?abstract?String?buildWheel();
?????//建造引擎
?????protected?abstract?String?buildEngine();
}
看到Blueprint類了,它中文的意思是“藍圖”,你要建造一輛車必須有一個設計樣稿或者藍圖吧,否則怎么生產?怎么裝配?該類就是一個可參考的生產樣本,如代碼清單30-26所示。
代碼清單30-26 生產藍圖
public?class?Blueprint?{
?????//車輪的要求
?????private?String?wheel;
?????//引擎的要求
?????private?String?engine;
?????public?String?getWheel()?{
?????????????return?wheel;
?????}
?????public?void?setWheel(String?wheel)?{
?????????????this.wheel?=?wheel;
?????}
?????public?String?getEngine()?{
?????????????return?engine;
?????}
?????public?void?setEngine(String?engine)?{
?????????????this.engine?=?engine;
?????}?????
}
這和一個具體的產品Car類是一樣的?錯,不一樣!它是一個藍圖,是一個可以參考的模板,有一個藍圖可以設計出非常多的產品,如有一個R系統的奔馳商務車設計藍圖,我們就可以生產出一系列的奔馳車。它指導我們的產品生產,而不是一個具體的產品。我們來看寶馬車建造車間,如代碼清單30-27所示。
代碼清單30-27 寶馬車建造車間
public?class?BMWBuilder?extends?CarBuilder?{
?????public?String?buildEngine()?{
?????????????return?super.getBlueprint().getEngine();
?????}
?????public?String?buildWheel()?{
?????????????return?super.getBlueprint().getWheel();
?????}
}
這是非常簡單的類。只要獲得一個藍圖,然后按照藍圖制造引擎和車輪即可,剩下的事情就交給抽象的建造者進行裝配。奔馳車間與此類似,如代碼清單30-28所示。
代碼清單30-28 奔馳車建造車間
public?class?BenzBuilder?extends?CarBuilder?{
?????public?String?buildEngine()?{
?????????????return?super.getBlueprint().getEngine();
?????}
?????public?String?buildWheel()?{
?????????????return?super.getBlueprint().getWheel();
?????}
}
兩個建造車間都已經完成,那現在的問題就變成了怎么讓車間運作,誰來編寫藍圖?誰來協調生產車間?誰來對外提供最終產品?于是導演類出場了,它不僅僅有每個車間需要的設計藍圖,還具有指導不同車間裝配順序的職責,如代碼清單30-29所示。
代碼清單30-29 導演類
public?class?Director?{
?????//聲明對建造者的引用
?????private?CarBuilder?benzBuilder?=?new?BenzBuilder();
?????private?CarBuilder?bmwBuilder?=?new?BMWBuilder();
?????//生產奔馳SUV
?????public?ICar?createBenzSuv(){
?????????????//制造出汽車
?????????????return?createCar(benzBuilder,?"benz的引擎",?"benz的輪胎");
?????}
?????//生產出一輛寶馬商務車
?????public?ICar?createBMWVan(){
?????????????return?createCar(benzBuilder,?"BMW的引擎",?"BMW的輪胎");
?????}
?????//生產出一個混合車型
?????public?ICar?createComplexCar(){??????????
?????????????return?createCar(bmwBuilder,?"BMW的引擎",?"benz的輪胎");
?????}
?????//生產車輛
?????private?ICar?createCar(CarBuilder?_carBuilder,String?engine,String?wheel){
?????????????//導演懷揣藍圖
?????????????Blueprint?bp?=?new?Blueprint();
?????????????bp.setEngine(engine);
?????????????bp.setWheel(wheel);
?????????????System.out.println("獲得生產藍圖");
?????????????_carBuilder.receiveBlueprint(bp);
?????????????return?_carBuilder.buildCar();
?????}
}
這里有一個私有方法createCar,其作用是減少導演類中的方法對藍圖的依賴,全部由該方法來完成。我們編寫一個場景類,如代碼清單30-30所示。
代碼清單30-30 場景類
public?class?Client?{
?????public?static?void?main(String[]?args)?{
?????????????//定義出導演類
?????????????Director?director?=new?Director();
?????????????//給我一輛奔馳車SUV
?????????????System.out.println("===制造一輛奔馳SUV===");
?????????????ICar?benzSuv?=?director.createBenzSuv();
?????????????System.out.println(benzSuv);
?????????????//給我一輛寶馬商務車
?????????????System.out.println("\n===制造一輛寶馬商務車===");
?????????????ICar?bmwVan?=?director.createBMWVan();
?????????????System.out.println(bmwVan);
?????????????//給我一輛混合車型
?????????????System.out.println("\n===制造一輛混合車===");
?????????????ICar?complexCar?=?director.createComplexCar();
?????????????System.out.println(complexCar);
?????}
}
場景類只要找到導演類(也就是車間主任了)說給我制造一輛這樣的寶馬車,車間主任馬上通曉你的意圖,設計了一個藍圖,然后命令建造車間拼命加班加點建造,最終返回給你一件最新出品的產品,運行結果如下所示:
===制造一輛奔馳SUV===
獲得生產藍圖
車的輪子是:benz的輪胎
車的引擎是:benz的引擎
===制造一輛寶馬商務車===
獲得生產藍圖
車的輪子是:BMW的輪胎
車的引擎是:BMW的引擎
===制造一輛混合車===
獲得生產藍圖
車的輪子是:benz的輪胎
車的引擎是:BMW的引擎
注意最后一個運行結果片段,我們可以立刻生產出一輛混合車型,只要有設計藍圖,這非常容易實現。反觀我們的抽象工廠模式,它是不可能實現該功能的,因為它更關注的是整體,而不關注到底用的是奔馳引擎還是寶馬引擎,而我們的建造者模式卻可以很容易地實現該設計,市場信息變更了,我們就可以立刻跟進,生產出客戶需要的產品。
30.2.3 最佳實踐
注意看上面的描述,我們在抽象工廠模式中使用“工廠”來描述構建者,而在建造者模式中使用“車間”來描述構建者,其實我們已經在說它們兩者的區別了,抽象工廠模式就好比是一個一個的工廠,寶馬車工廠生產寶馬SUV和寶馬VAN,奔馳車工廠生產奔馳車SUV和奔馳VAN,它是從一個更高層次去看對象的構建,具體到工廠內部還有很多的車間,如制造引擎的車間、裝配引擎的車間等,但這些都是隱藏在工廠內部的細節,對外不公布。也就是對領導者來說,他只要關心一個工廠到底是生產什么產品的,不用關心具體怎么生產。而建造者模式就不同了,它是由車間組成,不同的車間完成不同的創建和裝配任務,一個完整的汽車生產過程需要引擎制造車間、引擎裝配車間的配合才能完成,它們配合的基礎就是設計藍圖,而這個藍圖是掌握在車間主任(導演類)手中,它給建造車間什么藍圖就能生產什么產品,建造者模式更關心建造過程。雖然從外界看來一個車間還是生產車輛,但是這個車間的轉型是非常快的,只要重新設計一個藍圖,即可產生不同的產品,這有賴于建造者模式的功勞。
相對來說,抽象工廠模式比建造者模式的尺度要大,它關注產品整體,而建造者模式關注構建過程,因此建造者模式可以很容易地構建出一個嶄新的產品,只要導演類能夠提供具體的工藝流程。也正因為如此,兩者的應用場景截然不同,如果希望屏蔽對象的創建過程,只提供一個封裝良好的對象,則可以選擇抽象工廠方法模式。而建造者模式可以用在構件的裝配方面,如通過裝配不同的組件或者相同組件的不同順序,可以產生出一個新的對象,它可以產生一個非常靈活的架構,方便地擴展和維護系統。
- 前言
- 第一部分 大旗不揮,誰敢沖鋒——6大設計原則全新解讀
- 第1章 單一職責原則
- 1.2 絕殺技,打破你的傳統思維
- 1.3 我單純,所以我快樂
- 1.4 最佳實踐
- 第2章 里氏替換原則
- 2.2 糾紛不斷,規則壓制
- 2.3 最佳實踐
- 第3章 依賴倒置原則
- 3.2 言而無信,你太需要契約
- 3.3 依賴的三種寫法
- 3.4 最佳實踐
- 第4章 接口隔離原則
- 4.2 美女何其多,觀點各不同
- 4.3 保證接口的純潔性
- 4.4 最佳實踐
- 第5章 迪米特法則
- 5.2 我的知識你知道得越少越好
- 5.3 最佳實踐
- 第6章 開閉原則
- 6.2 開閉原則的廬山真面目
- 6.3 為什么要采用開閉原則
- 6.4 如何使用開閉原則
- 6.5 最佳實踐
- 第二部分 真刀實槍 ——23種設計模式完美演繹
- 第7章 單例模式
- 7.2 單例模式的定義
- 7.3 單例模式的應用
- 7.4 單例模式的擴展
- 7.5 最佳實踐
- 第8章 工廠方法模式
- 8.2 工廠方法模式的定義
- 8.3 工廠方法模式的應用
- 8.4 工廠方法模式的擴展
- 8.5 最佳實踐
- 第9章 抽象工廠模式
- 9.2 抽象工廠模式的定義
- 9.3 抽象工廠模式的應用
- 9.4 最佳實踐
- 第10章 模板方法模式
- 10.2 模板方法模式的定義
- 10.3 模板方法模式的應用
- 10.4 模板方法模式的擴展
- 10.5 最佳實踐
- 第11章 建造者模式
- 11.2 建造者模式的定義
- 11.3 建造者模式的應用
- 11.4 建造者模式的擴展
- 11.5 最佳實踐
- 第12章 代理模式
- 12.2 代理模式的定義
- 12.3 代理模式的應用
- 12.4 代理模式的擴展
- 12.5 最佳實踐
- 第13章 原型模式
- 13.2 原型模式的定義
- 13.3 原型模式的應用
- 13.4 原型模式的注意事項
- 13.5 最佳實踐
- 第14章 中介者模式
- 14.2 中介者模式的定義
- 14.3 中介者模式的應用
- 14.4 中介者模式的實際應用
- 14.5 最佳實踐
- 第15章 命令模式
- 15.2 命令模式的定義
- 15.3 命令模式的應用
- 15.4 命令模式的擴展
- 15.5 最佳實踐
- 第16章 責任鏈模式
- 16.2 責任鏈模式的定義
- 16.3 責任鏈模式的應用
- 16.4 最佳實踐
- 第17章 裝飾模式
- 17.2 裝飾模式的定義
- 17.3 裝飾模式應用
- 17.4 最佳實踐
- 第18章 策略模式
- 18.2 策略模式的定義
- 18.3 策略模式的應用
- 18.4 策略模式的擴展
- 18.5 最佳實踐
- 第19章 適配器模式
- 19.2 適配器模式的定義
- 19.3 適配器模式的應用
- 19.4 適配器模式的擴展
- 19.5 最佳實踐
- 第20章 迭代器模式
- 20.2 迭代器模式的定義
- 20.3 迭代器模式的應用
- 20.4 最佳實踐
- 第21章 組合模式
- 21.2 組合模式的定義
- 21.3 組合模式的應用
- 21.4 組合模式的擴展
- 21.5 最佳實踐
- 第22章 觀察者模式
- 22.2 觀察者模式的定義
- 22.3 觀察者模式的應用
- 22.4 觀察者模式的擴展
- 22.5 最佳實踐
- 第23章 門面模式
- 23.2 門面模式的定義
- 23.3 門面模式的應用
- 23.4 門面模式的注意事項
- 23.5 最佳實踐
- 第24章 備忘錄模式
- 24.2 備忘錄模式的定義
- 24.3 備忘錄模式的應用
- 24.4 備忘錄模式的擴展
- 24.5 最佳實踐
- 第25章 訪問者模式
- 25.2 訪問者模式的定義
- 25.3 訪問者模式的應用
- 25.4 訪問者模式的擴展
- 25.5 最佳實踐
- 第26章 狀態模式
- 26.2 狀態模式的定義
- 26.3 狀態模式的應用
- 第27章 解釋器模式
- 27.2 解釋器模式的定義
- 27.3 解釋器模式的應用
- 27.4 最佳實踐
- 第28章 享元模式
- 28.2 享元模式的定義
- 28.3 享元模式的應用
- 28.4 享元模式的擴展
- 28.5 最佳實踐
- 第29章 橋梁模式
- 29.2 橋梁模式的定義
- 29.3 橋梁模式的應用
- 29.4 最佳實踐
- 第三部分 誰的地盤誰做主 ——設計模式PK
- 第30章 創建類模式大PK
- 30.1 工廠方法模式VS建造者模式
- 30.2 抽象工廠模式VS建造者模式
- 第31章 結構類模式大PK
- 31.1 代理模式VS裝飾模式
- 31.2 裝飾模式VS適配器模式
- 第32章 行為類模式大PK
- 32.1 命令模式VS策略模式
- 32.2 策略模式VS狀態模式
- 32.3 觀察者模式VS責任鏈模式
- 第33章 跨戰區PK
- 33.1 策略模式VS橋梁模式
- 33.2 門面模式VS中介者模式
- 33.3 包裝模式群PK
- 第四部分 完美世界 ——設計模式混編
- 第34章 命令模式+責任鏈模式
- 34.2 混編小結
- 第35章 工廠方法模式+策略模式
- 35.2 混編小結
- 第36章 觀察者模式+中介者模式
- 36.2 混編小結
- 第五部分 擴展篇
- 第37章 MVC框架
- 37.2 最佳實踐
- 第38章 新模式
- 38.1 規格模式
- 38.2 對象池模式
- 38.3 雇工模式
- 38.4 黑板模式
- 38.5 空對象模式
- 附錄 23種設計模式彩圖