設計模式讀書筆記-----工廠方法模式
一、問題
在前一章《設計模式讀書筆記-----簡單工廠模式》中通過披薩的例項介紹了簡單工廠模式。在披薩例項中,如果我想根據地域的不同生產出不同口味的披薩,如紐約口味披薩,芝加哥口味披薩。如果利用簡單工廠模式,我們需要兩個不同的工廠,NYPizzaFactory、ChicagoPizzaFactory。在該地域中有很多的披薩店,他們並不想依照總店的製作流程來生成披薩,而是希望採用他們自己的製作流程。這個時候如果還使用簡單工廠模式,因為簡單工廠模式是將披薩的製作流程完全承包了。那麼怎麼辦?
二、解決方案
我們可以這樣解決:將披薩的製作方法交給各個披薩店完成,但是他們只能提供製作完成的披薩,披薩的訂單處理仍然要交給披薩工廠去做。也就是說,我們將createPizza()方法放回到PizzaStore中,其他的部分還是保持不變。
三、基本定義
工廠方法模式定義了一個建立物件的介面,但由子類決定要例項化的類是哪一個。工廠方法模式讓例項化推遲到子類。
四、模式結構
工廠方法模式的UML結構圖:
Product:抽象產品。所有的產品必須實現這個共同的介面,這樣一來,使用這些產品的類既可以引用這個介面。而不是具體類。
ConcreteProduct:具體產品。
Creator:
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、在工廠方法模式中,建立者通常會包含依賴於抽象產品的程式碼,而這些抽象產品是、由子類建立的,建立者不需要真的知道在製作哪種具體產品。