創造類模式:工廠方法模式VS建造者模式。
工廠方法模式注重的是整體物件的建立方法,而建造者模式注重的是部件構件的過程,旨在通過一步一步地精確構造創建出一個複雜的物件。我們舉個簡單例子來說明兩者的差異,如要製造一個超人,如果使用工廠方法模式,直接產生出來的就是一個力大無窮、能夠飛翔、內褲外穿的超人;而如果使用建造者模式,則需要組裝手、頭、腳、軀幹等部分,然後再把內褲外穿,於是一個超人就產生了。
按工廠方法建造超人
首先,按照工廠方法模式創建出一個超人,按照年齡把超人分為兩種型別:成年超人和未成年超人。這是一個非常正宗的工廠方法模式,定義一個產品的介面,然後再定義兩個實現,通過超人制造工廠製造超人。想想看我們對超人最大印象是什麼?當然是他的超能力,我們以specialTalent方法來代表,先看抽象產品類,如下所示。
public interface ISuperMan {
/**
* 每個超人都有特殊技能
*/
void specialTalent();
}
產品的介面定義好了,我們再來看具體的產品。先看成年超人,很簡單,如下所示。
public class AdultSuperMan implements ISuperMan {
@Override
public void specialTalent() {
System.out.println("超人力大無窮");
}
}
未成年超人的程式碼如下所示。
public class ChildSuperMan implements ISuperMan { @Override public void specialTalent() { System.out.println("小超人的能力是刀槍不入、快速運動"); } }
產品都具備,那我們編寫一個工廠類,其意圖就是生產超人,具體是成年超人還是未成年超人,則由客戶端決定,如下所示。
public class SuperManFactory { /** * 定義一個生產超人的工廠 * * @param type * @return */ public static ISuperMan createSuperMan(String type) { // 根據輸入引數產生不同的超人 if ("adult".equalsIgnoreCase(type)) { // 生產成人超人 return new AdultSuperMan(); } else if ("child".equalsIgnoreCase(type)) { // 生成未成年超人 return new ChildSuperMan(); } else { return null; } } }
產品有了,工廠類也有了,剩下的工廠就是開始生產超人。這也非常簡單,如下所示。
public class Client {
/**
* 場景類
*
* @param args
*/
public static void main(String[] args) {
// 生產一個成年超人
ISuperMan adultSuperMan = SuperManFactory.createSuperMan("adult");
// 展示一下超人的技能
adultSuperMan.specialTalent();
}
}
建立了一個超人生產工廠,年復一年的生產超人,對於具體生產出的產品,不管是成年超人還是未成年超人,都是一個模樣:深藍色緊身衣、胸前S標記、內褲外穿,沒有特殊的地方。但是我們的目的達到了——生產出超人,拯救全人類,這就是我們的意圖。具體怎麼生產、怎麼組裝,這不是工廠方法模式要考慮的,也就是說,工廠模式關注的是一個產品整體,生產出的產品應該具有相似的功能和架構。
注意:通過工廠方法模式生產出物件,然後由客戶端進行物件的其他操作,但是並不代表所有生產出的物件都必須具有相同的狀態和行為,他是由產品所決定。
按建造者模式建造超人
我們再來看看建造者模式是如何生產超人的,我們在抽象建造者上使用了模板方法模式,每一個建造者都必須返回一個產品,但是產品是如何製造的,則由各個建造者自己負責。我們來看看程式,先看產品類,如下所示。
public class SuperMan {
// 超人的軀體
private String body;
// 超人的特殊技能
private String specialTalent;
// 超人的標誌
private String specialSymbol;
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
public String getSpecialTalent() {
return specialTalent;
}
public void setSpecialTalent(String specialTalent) {
this.specialTalent = specialTalent;
}
public String getSpecialSymbol() {
return specialSymbol;
}
public void setSpecialSymbol(String specialSymbol) {
this.specialSymbol = specialSymbol;
}
}
超人這個產品是由三部分組成:軀體、特殊技能、身份標記,這就類似於電子產品,首先生產出一個韌體,然後再安裝一個靈魂(軟體驅動),最後再打上產品標籤。完事了!一個嶄新的產品就誕生了!我們的超人也是這樣生產的,先生產一個普通的軀體,然後注入特殊技能,最後打上S標籤,一個超人生產完畢。我們再來看一下建造者的抽象定義,如下所示。
public abstract class Builder {
// 定義一個超人的應用
protected final SuperMan superMan = new SuperMan();
/**
* 構造出超人軀體
*
* @param body
*/
public void setBody(String body) {
this.superMan.setBody(body);
}
/**
* 構建出超人的特殊技能
*
* @param st
*/
public void setSpecialTalent(String st) {
this.superMan.setSpecialTalent(st);
}
/**
* 購機拿出超人的特殊標記
*
* @param ss
*/
public void setSpecialSymbol(String ss) {
this.superMan.setSpecialSymbol(ss);
}
/**
* 構建出一個完整的超人
*
* @return
*/
public abstract SuperMan getSuperMan();
}
一個典型的模板方法模式,超人的各個部件(軀體、靈魂、標誌)都準備好了,具體怎麼組裝則是由實現類來決定。我們先來看成年超人,如下所示。
public class AdultSuperManBuilder extends Builder {
@Override
public SuperMan getSuperMan() {
super.setBody("強壯的軀體");
super.setSpecialTalent("會飛行");
super.setSpecialSymbol("胸前帶S標記");
return super.superMan;
}
}
怎麼回事?在這裡怎麼把模板方法模式遷移到建造者了?設計模式只是提供了一個解決問題的意圖:複雜物件的構件與他的表示分離,而沒有具體定出一個設計模式必須是這樣的實現,必須是這樣的程式碼,靈活運用模式才是其根本,別學死板了。
我們繼續看未成年超人的建造者,如下所示。
public class ChildSuperManBuilder extends Builder {
@Override
public SuperMan getSuperMan() {
super.setBody("強壯的軀體");
super.setSpecialTalent("刀槍不入");
super.setSpecialSymbol("胸前帶小S標記");
return super.superMan;
}
}
大家注意看我們這兩個具體的建造者,他們都關注了產品的各個部分,在某些應用場景下甚至會關心產品的構件順序,即使是相同的部件,裝配順序不同,產生的結果也不同,這也正是建造者模式的意圖:通過不同的部件、不同裝備產生不同的複雜物件。我們再來看導演類,如下所示。
public class Director {
// 兩個建造者的應用
private static Builder adultBuilder = new AdultSuperManBuilder();
// 未成年人的建造者
private static Builder childBuilder = new ChildSuperManBuilder();
/**
* 建造一個成年、會飛行的超人
*
* @return
*/
public static SuperMan getAdultSuperMan() {
return adultBuilder.getSuperMan();
}
/**
* 建造一個未成年、刀槍不入的超人
*
* @return
*/
public static SuperMan getChildSuperMan() {
return childBuilder.getSuperMan();
}
}
這很簡單,不多說了!看看場景類是如何呼叫的,如下所示。
public class Client {
public static void main(String[] args) {
// 建造一個成年超人
SuperMan adultSuperMan = Director.getAdultSuperMan();
// 展示一下超人的資訊
System.out.println(adultSuperMan.getSpecialTalent());
}
}
這個場景類的寫法與工廠方法模式是相同的,但是你可以看到,在建立超人的過程中,建造者必須關注超人的各個部件,而工廠方法模式則只關注超人的整體,這就是兩者的區別。
最佳實踐
工廠方法模式和建造者模式都屬於物件建立類模式,都用來建立類的物件。但他們之間的區別還是比較明顯的。
-
意圖不同
在工廠方法模式裡,我們關注的是一個產品整體,如超人整體,無須關心產品的各部分是如何創建出來的;但在建造者模式中,一個具體產品的產生是依賴各個部件的產生以及裝配順序,他關注的是“由零件一件一件的組裝出產品物件”。簡單的說,工廠模式是一個物件建立的粗線條應用,建造者模式則是通過細線條勾勒出一個複雜物件,關注的是產品組成部分的建立過程。
-
產品的複雜度不同
工廠方法模式建立的產品一般都是單一性質產品,如成年超人,都是一個模樣,而建造者模式建立的則是一個複合產品,他由各個部件複合而成,部件不同產品物件當然不同。這不是說工廠方法模式建立的物件簡單,而是指他們的粒度大小不同。一般來說,工廠方法模式的物件粒度比較粗,建造者模式的產品物件粒度比較細。
兩者的區別有了,那在具體的應用中,我們該如何選擇呢?是用工廠方法模式來建立物件,還是用建造者模式來建立物件,這完全取決於我們在做系統設計時的意圖,如果需要詳細關注一個產品部件的生產、安裝步驟,則選擇建造者,否則選擇工廠方法模式。