設計模式| 04 工廠方法模式
- 2019 年 10 月 6 日
- 筆記
開頭說幾句 部落客的部落格地址:https://www.jeffcc.top/ 推薦入門學習設計模式java版本的數據Head First 《設計模式》
什麼是工廠方法模式
專業定義:工廠方法模式定義了一個創建對象的介面,但是由子類決定實例化的類是哪一個。工廠方法讓實例化延遲到了子類。 部落客的理解:工廠方法其實也是使用了把不變的部分和變化的部分分開封裝起來的設計原則,將變化的部分提供一個抽象方法來交給子類來實現,不變的部分由工廠完成封裝。 這種設計可以將對象的創建封裝起來,以便得到更加松耦合,更有彈性的設計。
設計原則
- 依賴倒置原則,要依賴抽象,不要依賴具體類,具體做法是需要高層組件(工廠)和底層組件(實現類)之間不要有太多的依賴關係,而是可以通過一個共同的抽象類(工廠產生的對象)來實現依賴倒置。
- 多用組合,少用繼承。
- 針對介面編程,而不是針對實現編程。
- 為交互對象之間的松耦合設計而努力。
- 類應該對外開放拓展,對修改關閉。
設計要點
- 所有的工廠都是用來封裝對象的創建的。
- 工廠方法使用繼承,把對象的創建委託給子類,子類實現工廠方法來創建對象。
- 工廠方法允許類的實例化延遲到子類。
設計實現
設計背景
一家披薩店需要拓展加盟業務,屆時會有許多不同地方口味的披薩出現,而我們需要做的就是設計一個工廠來讓加盟商使用,具體的風味的披薩由加盟商自己決定,工廠只負責包裝等內容。
程式碼實現
項目結構

項目類圖

披薩工廠
package factory; import pizza.Pizza; /** * 披薩工廠 */ public abstract class PizzaStore { public Pizza orderPizza(String type){ Pizza pizza; // 進行創建pizza pizza = createPizza(type); // 下面進行的是共同的東西,所以封裝起來到工廠中 pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } /** * 抽取出一個抽象的製作pizza的方法來交給子類具體實現 * 工廠模式的關鍵所在 * @param type 什麼風味的pizza * @return */ public abstract Pizza createPizza(String type); }
ChicagoPizzaStore加盟店
package store; import factory.PizzaStore; import pizza.Pizza; import pizza.impl.ChicagoStyleCheesePizza; /** * ChicagoPizzaStore加盟店 */ public class ChicagoPizzaStore extends PizzaStore { /** * 模擬測試生產pizza 太多類型了就寫一個 理論可以無限拓展 * @param type 什麼風味的pizza * @return */ @Override public Pizza createPizza(String type) { if ("cheese".equals(type)) { return new ChicagoStyleCheesePizza(); } else { return null; } } }
NYpizza加盟店
package store; import factory.PizzaStore; import pizza.Pizza; import pizza.impl.NYStyleCheesePizza; /** * 紐約pizza加盟店 */ public class NYPizzaStore extends PizzaStore { /** * 模擬測試生產pizza 太多類型了就寫一個 理論可以無限拓展 * @param type 什麼風味的pizza * @return */ @Override public Pizza createPizza(String type) { if ("cheese".equals(type)) { return new NYStyleCheesePizza(); } else { return null; } } }
披薩抽象類(關鍵)
package pizza; import java.util.ArrayList; /** * pizza的抽象類 * 也就是之前說的依賴倒置所依賴的類!!! * 需要有pizza的操作的方法 */ public abstract class Pizza { public String name;//pizza名稱 public String dough;//pizza麵糰類型 public String suace;//pizza醬料類型 public ArrayList<String> toppings = new ArrayList<>(); //佐料 /** * 提供默認的基本做法 進行烘烤、切片、裝盒 如果需要可以進行重寫 */ public void prepare() { System.out.println("Preparing" + name); System.out.println("Tossing dough..."); System.out.println("Adding toppings:"); toppings.forEach(topping -> { System.out.println(" " + topping); }); } /** * 烘烤 */ 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; } }
ChicagoStyleCheesePizza
package pizza.impl; import pizza.Pizza; /** * 實現不同的風味的pizza ChicagoStyleCheesePizza */ public class ChicagoStyleCheesePizza extends Pizza { /** * 設置風味 */ public ChicagoStyleCheesePizza() { name = "Chicago Style Deep Dish Cheese Pizza"; dough = "Extra Thick Curst Dough"; suace = "Plum Tomato Suace"; toppings.add("Shredded Mozzarella Cheese"); } /** * 模擬需要有不同的切片方法 */ @Override public void cut() { System.out.println("Cutting the pizza into square slices"); } }
NYStyleCheesePizza
package pizza.impl; import pizza.Pizza; /** * 實現不同的風味的pizza 紐約風味 */ public class NYStyleCheesePizza extends Pizza { /** * 設置風味 */ public NYStyleCheesePizza() { name = "NY Style Sauce and Cheese Pizza"; dough="Thin Curst Dough"; suace="Marinara Suace"; toppings.add("Grated Reggiano Cheese"); } }
測試買pizza
package test; import factory.PizzaStore; import pizza.Pizza; import store.ChicagoPizzaStore; import store.NYPizzaStore; public class BuyPizza { public static void main(String[] args) { // 找到NY店 PizzaStore nyPizzaStore = new NYPizzaStore(); Pizza pizza1 = nyPizzaStore.orderPizza("cheese"); System.out.println("jay order a "+pizza1.getName()+"n"); // Chicago店 PizzaStore chicagoPizzaStore = new ChicagoPizzaStore(); Pizza pizza2 = chicagoPizzaStore.orderPizza("cheese"); System.out.println("jeff order a "+pizza2.getName()+"n"); } }
輸出結果

最後說兩句
工廠方法讓子類決定要實例化的類是哪一個,這句話的理解並不是指模式允許子類本身在運行時做出決定,而是要在編寫創建者類的時候,不需要知道實際所創建的產品是哪一個,選擇了使用哪一個子類,也就「決定」了實際創建的產品是什麼。 接下來會出一篇關於抽象工廠模式的文章 和工廠方法模式有一定的相通性。