1. 程式人生 > >設計模式讀書筆記-----工廠方法模式

設計模式讀書筆記-----工廠方法模式

        一、問題                                                                                                                        

在前一章《設計模式讀書筆記-----簡單工廠模式》中通過披薩的例項介紹了簡單工廠模式。在披薩例項中,如果我想根據地域的不同生產出不同口味的披薩,如紐約口味披薩,芝加哥口味披薩。如果利用簡單工廠模式,我們需要兩個不同的工廠,NYPizzaFactory、ChicagoPizzaFactory。在該地域中有很多的披薩店,他們並不想依照總店的製作流程來生成披薩,而是希望採用他們自己的製作流程。這個時候如果還使用簡單工廠模式,因為簡單工廠模式是將披薩的製作流程完全承包了。那麼怎麼辦?

        二、解決方案                                                                                                                 

我們可以這樣解決:將披薩的製作方法交給各個披薩店完成,但是他們只能提供製作完成的披薩,披薩的訂單處理仍然要交給披薩工廠去做。也就是說,我們將createPizza()方法放回到PizzaStore中,其他的部分還是保持不變。

        三、基本定義                                                                                                                  

工廠方法模式定義了一個建立物件的介面,但由子類決定要例項化的類是哪一個。工廠方法模式讓例項化推遲到子類。

        四、模式結構                                                                                                                  

工廠方法模式的UML結構圖:

        

Product:抽象產品。所有的產品必須實現這個共同的介面,這樣一來,使用這些產品的類既可以引用這個介面。而不是具體類。

ConcreteProduct:具體產品。

Creator:

抽象工廠。它實現了所有操縱產品的方法,但不實現工廠方法。Creator所有的子類都必須要實現factoryMethod()方法。

ConcreteCreator:具體工廠。製造產品的實際工廠。它負責建立一個或者多個具體產品,只有ConcreteCreator類知道如何建立這些產品。

工廠方法模式是簡單工廠模式的延伸。在工廠方法模式中,核心工廠類不在負責產品的建立,而是將具體的建立工作交給子類去完成。也就是後所這個核心工廠僅僅只是提供建立的介面,具體實現方法交給繼承它的子類去完成。當我們的系統需要增加其他新的物件時,我們只需要新增一個具體的產品和它的建立工廠即可,不需要對原工廠進行任何修改,這樣很好地符合了“開閉原則”。

        五、工廠方法模式實現

針對上面的解決方案,得到如下UML結構圖:

抽象產品類:Pizza.java

public abstract class Pizza {
	protected String name;        //名稱
	protected String dough;       //麵糰
	protected String sause;       //醬料
	protected List<String> toppings = new ArrayList<String>();       //佐料
	
	
	public void prepare() {
		System.out.println("Preparing "+name);
		System.out.println("Tossing dough");
		System.out.println("Adding sause");
		System.out.println("Adding toppings");
		for(int i = 0;i < toppings.size();i++){
			System.out.println("   "+toppings.get(i));
		}
	}

	public void bake() {
		System.out.println("Bake for 25 minutes at 350");
	}

	public void cut() {
		System.out.println("Cutting the pizza into diagonal slices");
	}

	public void box() {
		System.out.println("Place pizza in official PizzaStore box");
	}
	
	public String getName(){
		return name;
	}
}

具體產品類:NYStyleCheesePizza.java

public class NYStyleCheesePizza extends Pizza{
	public NYStyleCheesePizza(){
		name = "Ny Style Sauce and Cheese Pizza";
		dough = "Thin Crust Dough";
		sause = "Marinara Sauce";
		
		toppings.add("Crated Reggiano Cheese");
	}

}

ChicagoStyleCheesePizza.java

public class ChicagoStyleCheesePizza extends Pizza {
	public ChicagoStyleCheesePizza(){
		name = "Chicago Style Deep Dish Cheese Pizza";
		dough = "Extra Thick Crust Dough";
		sause = "Plum Tomato Sauce";
		
		toppings.add("Shredded Mozzarella Cheese");
	}
	
	public void cut(){
		System.out.println("Cutting the Pizza into square slices");
	}
}

抽象工廠:披薩總店。PizzaStore.java

public abstract class PizzaStore {
	public Pizza orderPizza(String type){
		Pizza pizza;
		pizza = createPizza(type);
		
		pizza.prepare();
		pizza.bake();
		pizza.cut();
		pizza.box();
		
		return pizza;
	}
	
	/*
	 * 建立pizza的方法交給子類去實現
	 */
	abstract Pizza createPizza(String type);
}

具體工廠。披薩分店。NYPizzaStore.java

public class NYPizzaStore extends PizzaStore{

	@Override
	Pizza createPizza(String item) {
		Pizza pizza = null;
		if("cheese".equals(item)){
			pizza = new NYStyleCheesePizza();
		}
		else if("veggie".equals(item)){
			pizza = new NYStyleVeggiePizza();
		}
		else if("clam".equals(item)){
			pizza = new NYStyleClamPizza();
		}
		else if("pepperoni".equals(item)){
			pizza = new NYStylePepperoniPizza();
		}
		
		return pizza;
	}
	

ChicagoPizzaStore.java

public class ChicagoPizzaStore extends PizzaStore {
	Pizza createPizza(String type) {
		Pizza pizza = null;
		if("cheese".equals(type)){
			pizza = new ChicagoStyleCheesePizza();
		}
		else if("clam".equals(type)){
			pizza = new ChicagoStyleClamPizza();
		}
		else if("pepperoni".equals(type)) {
			pizza = new ChicagoStylePepperoniPizza();
		}
		else if("veggie".equals(type)){
			pizza = new ChicagoStyleVeggiePizza();
		}
		return pizza;
	}

}

做了這麼多,應該可以吃披薩了吧。Ethan要一份紐約口味的披薩,Joel需要芝加哥口味的披薩。

PizzaTestDrive.java

public class PizzaTestDrive {
	public static void main(String[] args) {
		System.out.println("---------Joel 需要的芝加哥的深盤披薩---------");
		ChicagoPizzaStore chicagoPizzaStore = new ChicagoPizzaStore();       //建立芝加哥的披薩店
		Pizza joelPizza =chicagoPizzaStore.orderPizza("cheese");             //下訂單
		System.out.println("Joel ordered a " + joelPizza.getName() + "\n");
		
		System.out.println("---------Ethan 需要的紐約風味的披薩---------");
		NYPizzaStore nyPizzaStore = new NYPizzaStore();
		Pizza ethanPizza = nyPizzaStore.orderPizza("cheese");
		System.out.println("Ethan ordered a " + ethanPizza.getName() + "\n");
		
	}
}
執行結果。


        六、工廠方法模式的優缺點                                                                                           

優點

1、  在工廠方法中,使用者只需要知道所要產品的具體工廠,無須關係具體的建立過程,甚至不需要具體產品類的類名。

2、  在系統增加新的產品時,我們只需要新增一個具體產品類和對應的實現工廠,無需對原工廠進行任何修改,很好地符合了“開閉原則”。

缺點

1、  每次增加一個產品時,都需要增加一個具體類和物件實現工廠,是的系統中類的個數成倍增加,在一定程度上增加了系統的複雜度,同時也增加了系統具體類的依賴。這並不是什麼好事。

        七、工廠方法適用場景                                                                                                   

1、一個類不知道它所需要的物件的類。在工廠方法模式中,我們不需要具體產品的類名,我們只需要知道建立它的具體工廠即可。

2、一個類通過其子類來指定建立那個物件。在工廠方法模式中,對於抽象工廠類只需要提供一個建立產品的介面,而由其子類來確定具體要建立的物件,在程式執行時,子類物件將覆蓋父類物件,從而使得系統更容易擴充套件。

3、將建立物件的任務委託給多個工廠子類中的某一個,客戶端在使用時可以無須關心是哪一個工廠子類建立產品子類,需要時再動態指定。

        七、總結                                                                                                                        

         1、工廠方法模式完全符合“開閉原則”。

         2、工廠方法模式使用繼承,將物件的建立委託給子類,通過子類實現工廠方法來建立物件。

         3、工廠方法允許類將例項化延伸到子類進行。

         4、工廠方法讓子類決定要例項化的類時哪一個。在這裡我們要明白這並不是工廠來決定生成哪種產品,而是在編寫建立者類時,不需要知道實際建立的產品是哪個,選擇了使用哪個子類,就已經決定了實際建立的產品時哪個了。

         5、在工廠方法模式中,建立者通常會包含依賴於抽象產品的程式碼,而這些抽象產品是、由子類建立的,建立者不需要真的知道在製作哪種具體產品。