工厂与抽象工厂
本篇文章代碼:https://gitee.com/bithachi_admin_admin/mycode/tree/master/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/factory
一、工廠模式
1. 什么是工廠模式?
- 工廠模式(Factory Pattern)提供了一種創建對象的最佳方式,是 Java 中最常用的設計模式之一。這種類型的設計模式屬于創建型模式。
- 在工廠模式中,我們在創建對象時不會對客戶端暴露創建邏輯,并且是通過使用一個共同的接口來指向新創建的對象。
**意圖:**定義一個創建對象的接口,讓其子類自己決定實例化哪一個工廠類,工廠模式使其創建過程延遲到子類進行。(依賴倒置原則)
**主要解決:**主要解決接口選擇的問題。
**何時使用:**我們明確地計劃不同條件下創建不同實例時。
**如何解決:**讓其子類實現工廠接口,返回的也是一個抽象的產品。
**關鍵代碼:**創建過程在其子類執行。
應用實例:
- 您需要一輛汽車,可以直接從工廠里面提貨,而不用去管這輛汽車是怎么做出來的,以及這個汽車里面的具體實現。
- Mybatis換數據庫只需換方言和驅動就可以。
優點:
- 一個調用者想創建一個對象,只要知道其名稱就可以了。
- 擴展性高,如果想增加一個產品,只要擴展一個工廠類就可以。
- 屏蔽產品的具體實現,調用者只關心產品的接口。
**缺點:**每次增加一個產品時,都需要增加一個具體類和對象實現工廠,使得系統中類的個數成倍增加,在一定程度上增加了系統的復雜度,同時也增加了系統具體類的依賴。這并不是什么好事。
使用場景:
- 日志記錄器:記錄可能記錄到本地硬盤、系統事件、遠程服務器等,用戶可以選擇記錄日志到什么地方。
- 數據庫訪問,當用戶不知道最后系統采用哪一類數據庫,以及數據庫可能有變化時。
- 設計一個連接服務器的框架,需要三個協議,“POP3”、“IMAP”、“HTTP”,可以把這三個作為產品類,共同實現一個接口。
**注意事項:**作為一種創建類模式,在任何需要生成復雜對象的地方,都可以使用工廠方法模式。有一點需要注意的地方就是復雜對象適合使用工廠模式,而簡單對象,特別是只需要通過 new 就可以完成創建的對象,無需使用工廠模式。如果使用工廠模式,就需要引入一個工廠類,會增加系統的復雜度。
2. 加盟披薩店案例
案例描述:你有一家披薩店,經營有成,擊敗了許多的競爭者,現在你想開擴大市場,建立加盟店。因為區域的差異,每家加盟店都可能想要提供不同風味的比薩(比方說紐約、芝加哥、加州),這受到了開店地點及該地區比薩美食家口味的影響。那么如何設計呢?
下面是案例的UML模型設計圖:
PizzaStore:
public abstract class PizzaStore {abstract Pizza createPizza(String item);public Pizza orderPizza(String type) {Pizza pizza = createPizza(type);System.out.println("--- Making a " + pizza.getName() + " ---");pizza.prepare();pizza.bake();pizza.cut();pizza.box();return pizza;} }NYPizzaStore:
public class NYPizzaStore extends PizzaStore {@OverridePizza createPizza(String item) {if (item.equals("cheese")) {return new NYStyleCheesePizza();} else if (item.equals("veggie")) {return new NYStyleVeggiePizza();} else if (item.equals("clam")) {return new NYStyleClamPizza();} else if (item.equals("pepperoni")) {return new NYStylePepperoniPizza();} else {return null;}} }Pizza:
import java.util.ArrayList; public abstract class Pizza {String name;String dough;String sauce;ArrayList<String> toppings = new ArrayList<String>();void prepare() {System.out.println("Prepare " + name);System.out.println("Tossing dough...");System.out.println("Adding sauce...");System.out.println("Adding toppings: ");for (String topping : toppings) {System.out.println(" " + topping);}}void bake() {System.out.println("Bake for 25 minutes at 350");}void cut() {System.out.println("Cut the pizza into diagonal slices");}void box() {System.out.println("Place pizza in official PizzaStore box");}public String getName() {return name;}@Overridepublic String toString() {StringBuffer display = new StringBuffer();display.append("---- " + name + " ----\n");display.append(dough + "\n");display.append(sauce + "\n");for (String topping : toppings) {display.append(topping + "\n");}return display.toString();} }NYStyleCheesePizza**:**
public class NYStyleCheesePizza extends Pizza {public NYStyleCheesePizza() { name = "NY Style Sauce and Cheese Pizza";dough = "Thin Crust Dough";sauce = "Marinara Sauce";toppings.add("Grated Reggiano Cheese");} }披薩店測試:
public class PizzaTestDrive {public static void main(String[] args) {PizzaStore nyStore = new NYPizzaStore();PizzaStore chicagoStore = new ChicagoPizzaStore();Pizza pizza = nyStore.orderPizza("cheese");System.out.println("Ethan ordered a " + pizza.getName() + "\n");pizza = chicagoStore.orderPizza("cheese");System.out.println("Joel ordered a " + pizza.getName() + "\n");pizza = nyStore.orderPizza("clam");System.out.println("Ethan ordered a " + pizza.getName() + "\n");pizza = chicagoStore.orderPizza("clam");System.out.println("Joel ordered a " + pizza.getName() + "\n");pizza = nyStore.orderPizza("pepperoni");System.out.println("Ethan ordered a " + pizza.getName() + "\n");pizza = chicagoStore.orderPizza("pepperoni");System.out.println("Joel ordered a " + pizza.getName() + "\n");pizza = nyStore.orderPizza("veggie");System.out.println("Ethan ordered a " + pizza.getName() + "\n");pizza = chicagoStore.orderPizza("veggie");System.out.println("Joel ordered a " + pizza.getName() + "\n");} }運行結果:
--- Making a NY Style Sauce and Cheese Pizza --- Prepare NY Style Sauce and Cheese Pizza Tossing dough... Adding sauce... Adding toppings: Grated Reggiano Cheese Bake for 25 minutes at 350 Cut the pizza into diagonal slices Place pizza in official PizzaStore box Ethan ordered a NY Style Sauce and Cheese Pizza--- Making a Chicago Style Deep Dish Cheese Pizza --- Prepare 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--- Making a NY Style Clam Pizza --- Prepare NY Style Clam Pizza Tossing dough... Adding sauce... Adding toppings: Grated Reggiano CheeseFresh Clams from Long Island Sound Bake for 25 minutes at 350 Cut the pizza into diagonal slices Place pizza in official PizzaStore box Ethan ordered a NY Style Clam Pizza--- Making a Chicago Style Clam Pizza --- Prepare Chicago Style Clam Pizza Tossing dough... Adding sauce... Adding toppings: Shredded Mozzarella CheeseFrozen Clams from Chesapeake Bay Bake for 25 minutes at 350 Cutting the pizza into square slices Place pizza in official PizzaStore box Joel ordered a Chicago Style Clam Pizza--- Making a NY Style Pepperoni Pizza --- Prepare NY Style Pepperoni Pizza Tossing dough... Adding sauce... Adding toppings: Grated Reggiano CheeseSliced PepperoniGarlicOnionMushroomsRed Pepper Bake for 25 minutes at 350 Cut the pizza into diagonal slices Place pizza in official PizzaStore box Ethan ordered a NY Style Pepperoni Pizza--- Making a Chicago Style Pepperoni Pizza --- Prepare Chicago Style Pepperoni Pizza Tossing dough... Adding sauce... Adding toppings: Shredded Mozzarella CheeseBlack OlivesSpinachEggplantSliced Pepperoni Bake for 25 minutes at 350 Cutting the pizza into square slices Place pizza in official PizzaStore box Joel ordered a Chicago Style Pepperoni Pizza--- Making a NY Style Veggie Pizza --- Prepare NY Style Veggie Pizza Tossing dough... Adding sauce... Adding toppings: Grated Reggiano CheeseGarlicOnionMushroomsRed Pepper Bake for 25 minutes at 350 Cut the pizza into diagonal slices Place pizza in official PizzaStore box Ethan ordered a NY Style Veggie Pizza--- Making a Chicago Deep Dish Veggie Pizza --- Prepare Chicago Deep Dish Veggie Pizza Tossing dough... Adding sauce... Adding toppings: Shredded Mozzarella CheeseBlack OlivesSpinachEggplant Bake for 25 minutes at 350 Cutting the pizza into square slices Place pizza in official PizzaStore box Joel ordered a Chicago Deep Dish Veggie Pizza進程已結束,退出代碼0二、抽象工程模式
1. 什么是抽象工廠模式?
- 抽象工廠模式(Abstract Factory Pattern)是圍繞一個超級工廠創建其他工廠。該超級工廠為其他工廠的父工廠。這種類型的設計模式屬于創建型模式,它提供了一種創建對象的最佳方式。
- 在抽象工廠模式中,接口是負責創建一個相關對象的工廠,不需要顯式指定它們的類。每個生成的工廠都能按照工廠模式提供對象。
**意圖:**提供一個創建一系列相關或相互依賴對象的接口,而無需指定它們具體的類。
**主要解決:**主要解決接口選擇的問題。
**何時使用:**系統的產品有多于一個的產品族,而系統只消費其中某一族的產品。
**如何解決:**在一個產品族里面,定義多個產品。
**關鍵代碼:**在一個工廠里聚合多個同類產品。
**應用實例:**工作了,為了參加一些聚會,肯定有兩套或多套衣服吧,比如說有商務裝(成套,一系列具體產品)、時尚裝(成套,一系列具體產品),甚至對于一個家庭來說,可能有商務女裝、商務男裝、時尚女裝、時尚男裝,這些也都是成套的,即一系列具體產品。假設一種情況,在您的家中,某一個衣柜(具體工廠)只能存放某一種這樣的衣服(成套,一系列具體產品),每次拿這種成套的衣服時也自然要從這個衣柜中取出了。用 OOP 的思想去理解,所有的衣柜(具體工廠)都是衣柜類的(抽象工廠)某一個,而每一件成套的衣服又包括具體的上衣(某一具體產品),褲子(某一具體產品),這些具體的上衣其實也都是上衣(抽象產品),具體的褲子也都是褲子(另一個抽象產品)。
**優點:**當一個產品族中的多個對象被設計成一起工作時,它能保證客戶端始終只使用同一個產品族中的對象。
**缺點:**產品族擴展非常困難,要增加一個系列的某一產品,既要在抽象的 Creator 里加代碼,又要在具體的里面加代碼。
使用場景: 1、QQ 換皮膚,一整套一起換。 2、生成不同操作系統的程序。
**注意事項:**產品族難擴展,產品等級易擴展。
2. 給加盟披薩店新增原料工廠
案例描述:你有一家披薩店,經營有成,擊敗了許多的競爭者,現在你想開擴大市場,建立加盟店。因為區域的差異,每家加盟店都可能想要提供不同風味的比薩(比方說紐約、芝加哥、加州),這受到了開店地點及該地區比薩美食家口味的影響。那么如何設計呢?這是我們工廠模式的案例,現在我們在此案例上加點東西,我們希望所有的加盟店都使用統一的調料(原料):面團、醬料、臘腸、芝士、蔬菜、肉等。我們可以建造原料工廠來滿足這種需求。
下面是UML設計模型:
下面給出核心實現代碼;
**
**
披薩抽象類Pizza:
public abstract class Pizza {String name;Dough dough;Sauce sauce;Veggies veggies[];Cheese cheese;Pepperoni pepperoni;Clams clam;abstract void prepare();void bake() {System.out.println("Bake for 25 minutes at 350");}void cut() {System.out.println("Cutting the pizza into diagonal slices");}void box() {System.out.println("Place pizza in official PizzaStore box");}void setName(String name) {this.name = name;}String getName() {return name;}@Overridepublic String toString() {StringBuffer result = new StringBuffer();result.append("---- " + name + " ----\n");if (dough != null) {result.append(dough);result.append("\n");}if (sauce != null) {result.append(sauce);result.append("\n");}if (cheese != null) {result.append(cheese);result.append("\n");}if (veggies != null) {for (int i = 0; i < veggies.length; i++) {result.append(veggies[i]);if (i < veggies.length-1) {result.append(", ");}}result.append("\n");}if (clam != null) {result.append(clam);result.append("\n");}if (pepperoni != null) {result.append(pepperoni);result.append("\n");}return result.toString();} }具體的某種披薩類CheesePizza:
public class CheesePizza extends Pizza {//原料工廠PizzaIngredientFactory ingredientFactory;public CheesePizza(PizzaIngredientFactory ingredientFactory) {this.ingredientFactory = ingredientFactory;}@Overridevoid prepare() {System.out.println("Preparing " + name);dough = ingredientFactory.createDough();sauce = ingredientFactory.createSauce();cheese = ingredientFactory.createCheese();} }披薩店抽象類PizzaStore:
public abstract class PizzaStore {protected abstract Pizza createPizza(String item);public Pizza orderPizza(String type) {Pizza pizza = createPizza(type);System.out.println("--- Making a " + pizza.getName() + " ---");pizza.prepare();pizza.bake();pizza.cut();pizza.box();return pizza;} }紐約披薩店NYPizzaStore:
**
**
原料工廠抽象類****PizzaIngredientFactory:
public interface PizzaIngredientFactory {public Dough createDough();public Sauce createSauce();public Cheese createCheese();public Veggies[] createVeggies();public Pepperoni createPepperoni();public Clams createClam(); }具體的紐約原料工廠****NYPizzaIngredientFactory:
public class NYPizzaIngredientFactory implements PizzaIngredientFactory {@Overridepublic Dough createDough() {return new ThinCrustDough();}@Overridepublic Sauce createSauce() {return new MarinaraSauce();}@Overridepublic Cheese createCheese() {return new ReggianoCheese();}@Overridepublic Veggies[] createVeggies() {Veggies veggies[] = { new Garlic(), new Onion(), new Mushroom(), new RedPepper() };return veggies;}@Overridepublic Pepperoni createPepperoni() {return new SlicedPepperoni();}@Overridepublic Clams createClam() {return new FreshClams();} }某類型的具體原料產品****Mushroom:
public class Mushroom implements Veggies {@Overridepublic String toString() {return "Mushrooms";} }測試運行PizzaTestDrive:
public class PizzaTestDrive {public static void main(String[] args) {PizzaStore nyStore = new NYPizzaStore();PizzaStore chicagoStore = new ChicagoPizzaStore();Pizza pizza = nyStore.orderPizza("cheese");System.out.println("Ethan ordered a " + pizza + "\n");pizza = chicagoStore.orderPizza("cheese");System.out.println("Joel ordered a " + pizza + "\n");pizza = nyStore.orderPizza("clam");System.out.println("Ethan ordered a " + pizza + "\n");pizza = chicagoStore.orderPizza("clam");System.out.println("Joel ordered a " + pizza + "\n");pizza = nyStore.orderPizza("pepperoni");System.out.println("Ethan ordered a " + pizza + "\n");pizza = chicagoStore.orderPizza("pepperoni");System.out.println("Joel ordered a " + pizza + "\n");pizza = nyStore.orderPizza("veggie");System.out.println("Ethan ordered a " + pizza + "\n");pizza = chicagoStore.orderPizza("veggie");System.out.println("Joel ordered a " + pizza + "\n");} }運行結果:
--- Making a New York Style Cheese Pizza --- Preparing New York Style Cheese Pizza Bake for 25 minutes at 350 Cutting the pizza into diagonal slices Place pizza in official PizzaStore box Ethan ordered a ---- New York Style Cheese Pizza ---- Thin Crust Dough Marinara Sauce Reggiano Cheese--- Making a Chicago Style Cheese Pizza --- Preparing Chicago Style Cheese Pizza Bake for 25 minutes at 350 Cutting the pizza into diagonal slices Place pizza in official PizzaStore box Joel ordered a ---- Chicago Style Cheese Pizza ---- ThickCrust style extra thick crust dough Tomato sauce with plum tomatoes Shredded Mozzarella--- Making a New York Style Clam Pizza --- Preparing New York Style Clam Pizza Bake for 25 minutes at 350 Cutting the pizza into diagonal slices Place pizza in official PizzaStore box Ethan ordered a ---- New York Style Clam Pizza ---- Thin Crust Dough Marinara Sauce Reggiano Cheese Fresh Clams from Long Island Sound--- Making a Chicago Style Clam Pizza --- Preparing Chicago Style Clam Pizza Bake for 25 minutes at 350 Cutting the pizza into diagonal slices Place pizza in official PizzaStore box Joel ordered a ---- Chicago Style Clam Pizza ---- ThickCrust style extra thick crust dough Tomato sauce with plum tomatoes Shredded Mozzarella Frozen Clams from Chesapeake Bay--- Making a New York Style Pepperoni Pizza --- Preparing New York Style Pepperoni Pizza Bake for 25 minutes at 350 Cutting the pizza into diagonal slices Place pizza in official PizzaStore box Ethan ordered a ---- New York Style Pepperoni Pizza ---- Thin Crust Dough Marinara Sauce Reggiano Cheese Garlic, Onion, Mushrooms, Red Pepper Sliced Pepperoni--- Making a Chicago Style Pepperoni Pizza --- Preparing Chicago Style Pepperoni Pizza Bake for 25 minutes at 350 Cutting the pizza into diagonal slices Place pizza in official PizzaStore box Joel ordered a ---- Chicago Style Pepperoni Pizza ---- ThickCrust style extra thick crust dough Tomato sauce with plum tomatoes Shredded Mozzarella Black Olives, Spinach, Eggplant Sliced Pepperoni--- Making a New York Style Veggie Pizza --- Preparing New York Style Veggie Pizza Bake for 25 minutes at 350 Cutting the pizza into diagonal slices Place pizza in official PizzaStore box Ethan ordered a ---- New York Style Veggie Pizza ---- Thin Crust Dough Marinara Sauce Reggiano Cheese Garlic, Onion, Mushrooms, Red Pepper--- Making a Chicago Style Veggie Pizza --- Preparing Chicago Style Veggie Pizza Bake for 25 minutes at 350 Cutting the pizza into diagonal slices Place pizza in official PizzaStore box Joel ordered a ---- Chicago Style Veggie Pizza ---- ThickCrust style extra thick crust dough Tomato sauce with plum tomatoes Shredded Mozzarella Black Olives, Spinach, Eggplant進程已結束,退出代碼0三、比較工廠模式與抽象工廠模式
- 工廠方法使用繼承: 把對象的創建委托給子類,子類實現工廠方法來創建對象。
- 抽象工廠使用對象組合(在): 對象的創建被實現在工廠接口所暴露出來的方法中。
總結
- 上一篇: 设计模式开篇
- 下一篇: 3.1.1 存储器的分类(半导体-磁芯-