--- tags: Design Pattern,Creational Pattern title: Factory Method (4) --- {%hackmd Sy5Quc5JY %} # <font color="#F59F00">**Factory Method (4)**</font> 我們以 Pizza 作為例子解釋 * **Example** ```java public class PizzaStore { Pizza orderPizza(String type) { Pizza pizza; if(type.equals("cheese")) { pizza = new CheesePizza(); } else if(type.equals("greek")) { pizza = new GreekPizza(); } else if(type.equals("pepperoni")) { pizza = new PepperoniPizza(); } pizza.prepare(); pizza.cook() ; retrun pizza; } ``` ## <font color="#F59F00">**SimpleFactory Pattern**</font> 根據 ==**rule #1**== ,把 `orderPizza` 裡面的 if-else 拉出來到一個工廠裡面,這個工廠專門製作 `pizza`,**簡單工廠管理物件的創造,如果 client 要取得物件,只要給簡單工廠參數就可以**,然後在 `PizzaStore` 的 constructor 丟一個工廠進去就好。 ```java public class SimplePizzaFactory { public Pizza createPizza(String type) { Pizza pizza = null; if(type.equals("cheese")) { pizza = new CheesePizza(); } else if(type.equals("greek")) { pizza = new GreekPizza(); } else if(type.equals("pepperoni")) { pizza = new PepperoniPizza(); } return pizza; } } public class PizzaStore { SimplePizzaFactory factory; public PizzaStore(SimplePizzaFactory factory) { this.factory = factory; } public Pizza orderPizza(String type) { Pizza pizza; pizza = factory.creatPizza(tpye); pizza.prepare(); pizza.cook(); return pizza; } } ``` 簡單工廠模式,讓我們把 ==**pizza 的創造和 pizza 的使用分開了**==,減少了 client 對實作的依賴。<br> <span style="display:block;text-align:center;"><img src="https://www.jyt0532.com/public/factory1.png" width="600" height="380"></img></span> 今天生意不錯,開個分店。 同樣是 `cheese pizza`,但是 `NewYork` 和 `Chicago` 做法不一樣,修改 `SimplePizzaFactory` `createPizza` 多一個參數。 ```java public class SimplePizzaFactory { public Pizza createPizza(String style, String type) { Pizza pizza = null; if(style.equal("NewYork")) { if(type.equals("cheese")) { pizza = new NewYorkCheesePizza(); } else... } else if (style.equal("Chicago")) { if(style.equals("cheese")) { pizza = new ChicagoCheesePizza(); } else... } return pizza; } } ``` 然後讓 `NewYorkPizzaFactory` `ChicagoPizzaFactory` 都繼承 `SimplePizzaFactory` 因為我們現在要為不同 `style` 的 PizzaStore 建立不同的工廠。 今天點 NewYork 風味的起司 pizza ```java NewYorkPizzaFactory newYorkPizzaFactory = new NewYorkPizzaFactory(); PizzaStore newYorkStore = new PizzaStore(newYorkPizzaFactory); newYorkStore.orderPizza("cheese"); ``` 今天點 Chicago 風味的起司 pizza ```java ChicagoPizzaFactory chicagoPizzaFactory = new ChicagoPizzaFactory(); PizzaStore newYorkStore = new PizzaStore(chicagoPizzaFactory); chicagoStore.orderPizza("cheese"); ``` 但是現在在 Factory 之間我們使用了繼承,我們知道 `SimplePizzaFactory` 只是個介面,真正實作的是 subclass 的實體工廠。 ==**decouple + hierarchy 兩種功能同時需要時,我們就可以使用 Factory Method Pattern**== ## <font color="#F59F00">**Factory Method Pattern**</font> **工廠方法模式,定義了一個建立物件的介面,由子類決定實例化的類別為何。** 如果我們現在把 `createPizza` 放回 `PizzaStore` 讓子類別來決定怎麼 createPizza。 ```java public abstract class PizzaStore { public abstract Pizza createPizza(String type); public Pizza orderPizza(String type) { Pizza pizza = createPizza(type); pizza.prepare(); pizza.cook; return pizza; } } ``` 讓 NewYorkStore 繼承 PizzaStore,實作 createPizza ```java public class NewYorkPizzaStore extends PizzaStore { public createPizza(String type) { if(type.equals("cheese")) { return new NewYorkCheesePizza(); } else if(type.equals("veggle")) { return new NewYorkVegglePizza(); } else if (type.equals("clam")) { return new NewYorkClamPizza(); } else if (type.equals("pepperoni")) { return new NewYorkPepperoniPizza(); } else { return null; } } } ``` 原本物件的建立,交給一個外來的工廠處理,現在交給子類別處理,而且父類別還可以呼叫子類別實作的函式 `createPizza`,這種互相呼叫的函式通常依賴性很高,但是我們利用工廠讓父類別和子類別讓依賴變鬆散了。 套用工廠後,**也就是讓實作在 `PizzaStore` 的 subclass 實作,讓物件的製作和使用做分離。** ```java public abstract class PizzaStore { public abstract Pizza createPizza(String type); public Pizza orderPizza(String type) { Pizza pizza = createPizza(type); pizza.prepare(); pizza.cook; return pizza; } } public class NewYorkPizzaStore extends PizzaStore { public createPizza(String type) { if(type.equals("cheese")) { return new NewYorkCheesePizza(); } else if(type.equals("veggle")) { return new NewYorkVegglePizza(); } else if (type.equals("clam")) { return new NewYorkClamPizza(); } else if (type.equals("pepperoni")) { return new NewYorkPepperoniPizza(); } else { return null; } } } ``` order `pizza` 的方法 ```java PizzaStore pizzaStore = new NewYorkPizzaStore(); Pizza pizza = pizzaStore.orderPizza(); ``` * <font color="#92d1ff">**Factory Method 的結構**</font> <br><span style="display:block;text-align:center;"><img src="https://www.jyt0532.com/public/factory4.png" width="650" height="300"></img></span><br> * <font color="#98c37a">**Product : Pizza**</font> 定義 factoryMethod : createPizza 創造物件的介面。 * <font color="#98c37a">**ConcreteProduct : NewYorkCheesePizza/...**</font> 實作 Production。 * <font color="#98c37a">**Creator : PizzaStore**</font> 宣告 factoryMethod,必須回傳 Product 和宣告其他 client 需要用到的 API * <font color="#98c37a">**ConcreteCreator : NewYorkPizzaStore**</font> 實作 factoryMethod 回傳 ConcreteProduct 的 instance。 * <font color="#92d1ff">**Pros & Cons**</font> * <font color="#98c37a">**Pros**</font> 和簡單工廠一樣 1. 區隔物件的使用和生成 2. 隱藏了物件的細節 3. 加入新產品不用更改 Creator,直接繼承就好,client 一樣不用更改,完全符合 OCP。 * <font color="#98c37a">**Cons**</font> ConcreteCreator 和 ConcreteProduct 會成對的增加,如果今天想在加一個 `CaliforniaPizza` 就必須在定義完 `CaliforniaPizza` 後,再定義 CaliforniaPizzaFactory