工廠模式02之工廠方法模式
參考:Head First設計模式
概述
-
簡單工廠模式實現了生成產品類的程式碼與客戶端程式碼分離,在工廠類中可以新增生成產品的邏輯程式碼。
-
但是簡單工廠模式不符合“開放-封閉”原則。例如要加一個 新產品類,就要修改 工廠類 生成產品的邏輯程式碼,增加
if-else
判斷。對於這個問題,工廠方法模式可以解決。
定義
工廠方法模式 定義了一個建立物件的介面,但由子類決定要例項化的類是哪一個。工廠方法模式 讓類把例項化推遲到子類。
類圖
- 角色
- 抽象產品類 : Product
- 具體產品類 : ConcreteProductA 和 ConcreteProductB
- 抽象工廠類 : AbstractFactory
- 具體工廠類 : ConcreteFactoryA 和 ConcreteFactoryB
例項
還是以披薩店的披薩訂單為例,用工廠方法模式來處理披薩店的訂單。並且披薩店還開了加盟店,有紐約披薩店和芝加哥披薩店。
類圖
- 建立者類(抽象工廠類 + 具體工廠實現類)
- 產品類(抽象產品類 + 具體產品實現類)
程式碼實現
- 抽象工廠類
PizzaStore
package cn.edu.nwpu.factoryMethod;
/**
*
* @author yylin
*
*/
public abstract class PizzaStore {
public Pizza orderPizza(String type) {
Pizza pizza = null;
pizza = createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
// 抽象的工廠方法
abstract Pizza createPizza(String type);
}
- 具體工廠實現類1
NYPizzaStore
package cn.edu.nwpu.factoryMethod;
/**
*
* @author yylin
*
*/
public class NYStylePizzaStore extends PizzaStore {
@Override
Pizza createPizza(String type) {
Pizza pizza = null;
if(type.equals("cheese")) {
pizza = new NYStyleCheesePizza();
} else if(type.equals("prpperoni")) {
pizza = new NYStylePepperoniPizza();
} else if(type.equals("clam")) {
pizza = new NYStyleClamPizza();
} else if(type.equals("veggie")) {
pizza = new NYStyleVeggiePizza();
}
return pizza;
}
}
- 具體工廠實現類2
ChicagoPizzaStore
package cn.edu.nwpu.factoryMethod;
/**
*
* @author yylin
*
*/
public class ChicagoStylePizzaStore extends PizzaStore {
@Override
Pizza createPizza(String type) {
Pizza pizza = null;
if(type.equals("cheese")) {
pizza = new ChicagoStyleCheesePizza();
} else if(type.equals("prpperoni")) {
pizza = new ChicagoStylePepperoniPizza();
} else if(type.equals("clam")) {
pizza = new ChicagoStyleClamPizza();
} else if(type.equals("veggie")) {
pizza = new ChicagoStyleVeggiePizza();
}
return pizza;
}
}
- 抽象產品類
Pizza
package cn.edu.nwpu.factoryMethod;
import java.util.ArrayList;
/**
*
* @author yylin
*
*/
public abstract class Pizza {
/*
* 抽象類提供了預設基本做法, 準備工作以特定順序進行
*/
String name;
String dough;
String sauce;
ArrayList toppings = new ArrayList();
public void prepare() {
System.out.println("Preparing " + name);
System.out.println("Tossing dough...");
System.out.println("Adding sauce...");
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;
}
}
- 具體產品實現類1
NYStyleCheesePizza
package cn.edu.nwpu.factoryMethod;
/**
*
* @author yylin
*
*/
public class NYStyleCheesePizza extends Pizza {
/*
* 具體產品實現類中可以加入自己的特色,或者覆蓋Pizza類中的方法
*/
public NYStyleCheesePizza(){
name = "NY Style Sauce and Cheese Pizza";
dough = "Thin Crust Dough";
sauce = "Marinara Sauce";
toppings.add("Grated Reggiano Cheese");
}
}
- 具體產品實現類2
NYStylePepperoniPizza
package cn.edu.nwpu.factoryMethod;
/**
*
* @author yylin
*
*/
public class NYStylePepperoniPizza extends Pizza {
}
- 具體產品實現類3
NYStyleClamPizza
package cn.edu.nwpu.factoryMethod;
/**
*
* @author yylin
*
*/
public class NYStyleClamPizza extends Pizza {
}
- 具體產品實現類4
NYStyleVeggiePizza
package cn.edu.nwpu.factoryMethod;
/**
*
* @author yylin
*
*/
public class NYStyleVeggiePizza extends Pizza {
}
- 具體產品實現類5
ChicagoStyleCheesePizza
package cn.edu.nwpu.factoryMethod;
/**
*
* @author yylin
*
*/
public class ChicagoStyleCheesePizza extends Pizza {
/*
* 具體產品實現類中可以加入自己的特色,或者覆蓋Pizza類中的方法
*/
public ChicagoStyleCheesePizza() {
name = "";
dough = "";
sauce = "";
toppings.add("");
}
@Override
public void cut() {
System.out.println("Cutting the pizza into square slices");
}
}
- 具體產品實現類6
ChicagoStylePepperoniPizza
package cn.edu.nwpu.factoryMethod;
/**
*
* @author yylin
*
*/
public class ChicagoStylePepperoniPizza extends Pizza {
}
- 具體產品實現類7
ChicagoStyleClamPizza
package cn.edu.nwpu.factoryMethod;
/**
*
* @author yylin
*
*/
public class ChicagoStyleClamPizza extends Pizza {
}
- 具體產品實現類8
ChicagoStyleVeggiePizza
package cn.edu.nwpu.factoryMethod;
/**
*
* @author yylin
*
*/
public class ChicagoStyleVeggiePizza extends Pizza {
}
- 測試類
PizzaTestDrive
顧客Ethan想要紐約風格的乳酪披薩,顧客Joel想要芝加哥風格的乳酪披薩。
package cn.edu.nwpu.factoryMethod;
/**
*
* @author yylin
*
*/
public class PizzaTestDrive {
public static void main(String[] args) {
// 建立兩個不同的披薩店
PizzaStore nyStore = new NYStylePizzaStore();
PizzaStore chicagoStore = new ChicagoStylePizzaStore();
// 顧客Ethan的訂單
Pizza pizza = nyStore.orderPizza("cheese");
System.out.println("Ethan ordered a " + pizza.getName() + "\n");
// 顧客Joel的訂單
pizza = chicagoStore.orderPizza("cheese");
System.out.println("Joel ordered a " + pizza.getName() + "\n");
}
}
- 測試結果
Preparing NY Style Sauce and Cheese Pizza
Tossing dough...
Adding sauce...
Adding toppings:
Grated Reggiano Cheese
Bake for 25 minutes at 350
Cutting the pizza into diagonal slices
Place pizza in official PizzaStore box
Ethan ordered a NY Style Sauce and Cheese Pizza
Preparing Chicago Style Deep Dish Cheese Pizza
Tossing dough...
Adding sauce...
Adding toppings:
Shredded Mozzarella Cheese
Bake for 25 minutes at 350
Cutting the pizza into square slices
Place pizza in official PizzaStore box
Joel ordered a Chicago Style Deep Dish Cheese Pizza
小結
-
工廠方法模式又稱為工廠模式,也叫虛擬構造器(Virtual Constructor)模式或者多型工廠模式(Polymorphic Factory)。
-
在工廠方法模式中,父類負責定義建立物件的公共介面,而子類則負責生成具體的物件;
這樣做的目的是將類的例項化操作延遲到子類中完成;
即由子類來決定究竟應該例項化(建立)哪一個類。 -
工廠方法模式包含四個角色:
(1)抽象產品是定義產品的介面,是工廠方法模式所建立物件的超型別,即產品物件的共同父類或介面;
(2)具體產品實現了抽象產品介面,某種型別的具體產品由專門的具體工廠建立,它們之間往往一一對應;
(3)抽象工廠中聲明瞭工廠方法,用於返回一個產品,它是工廠方法模式的核心,任何在模式中建立物件的工廠類都必須實現該介面;
(4)具體工廠是抽象工廠類的子類,實現了抽象工廠中定義的工廠方法,並可由客戶呼叫,返回一個具體產品類的例項。 -
工廠方法模式是簡單工廠模式的進一步抽象和推廣。
由於使用了面向物件的多型性,工廠方法模式保持了簡單工廠模式的優點,而且克服了它的缺點。
在工廠方法模式中,核心的工廠類不再負責所有產品的建立,而是將具體建立工作交給子類去做。
這個核心類僅僅負責給出具體工廠必須實現的介面,而不負責產品類被例項化這種細節,這使得工廠方法模式可以允許系統在不修改工廠角色的情況下引進新產品。 -
工廠方法模式:
(1)主要優點:增加新的產品類時無須修改現有系統,並封裝了產品物件的建立細節,系統具有良好的靈活性和可擴充套件性;
(2)缺點在於增加新產品的同時需要增加新的工廠,導致系統類的個數成對增加,在一定程度上增加了系統的複雜性。 -
工廠方法模式適用情況包括:
(1)一個類不知道它所需要的物件的類;
(2)一個類通過其子類來指定建立哪個物件;
(3)將建立物件的任務委託給多個工廠子類中的某一個,客戶端在使用時可以無須關心是哪一個工廠子類建立產品子類,需要時再動態指定。