1. 程式人生 > >創造類模式:工廠方法模式VS建造者模式。

創造類模式:工廠方法模式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());
	}
}

這個場景類的寫法與工廠方法模式是相同的,但是你可以看到,在建立超人的過程中,建造者必須關注超人的各個部件,而工廠方法模式則只關注超人的整體,這就是兩者的區別。

最佳實踐

工廠方法模式和建造者模式都屬於物件建立類模式,都用來建立類的物件。但他們之間的區別還是比較明顯的。

  • 意圖不同

在工廠方法模式裡,我們關注的是一個產品整體,如超人整體,無須關心產品的各部分是如何創建出來的;但在建造者模式中,一個具體產品的產生是依賴各個部件的產生以及裝配順序,他關注的是“由零件一件一件的組裝出產品物件”。簡單的說,工廠模式是一個物件建立的粗線條應用,建造者模式則是通過細線條勾勒出一個複雜物件,關注的是產品組成部分的建立過程。

  • 產品的複雜度不同

工廠方法模式建立的產品一般都是單一性質產品,如成年超人,都是一個模樣,而建造者模式建立的則是一個複合產品,他由各個部件複合而成,部件不同產品物件當然不同。這不是說工廠方法模式建立的物件簡單,而是指他們的粒度大小不同。一般來說,工廠方法模式的物件粒度比較粗,建造者模式的產品物件粒度比較細。

兩者的區別有了,那在具體的應用中,我們該如何選擇呢?是用工廠方法模式來建立物件,還是用建造者模式來建立物件,這完全取決於我們在做系統設計時的意圖,如果需要詳細關注一個產品部件的生產、安裝步驟,則選擇建造者,否則選擇工廠方法模式。