Design Pattern(4) - Factory Method
April 28, 2017以下文章是閱讀 深入淺出Design Pattern 還有 聖經還有Source making的筆記 圖片截圖自lynda.com的Foundations of Programming: Design Patterns 要更深入的理解一定要去看這兩本書
來點個Pizza吧
今天想設計一個PizzaStore 裡面可以點Pizza
嗯 看起來不差 compile-time的時候pizza.prepare()跟pizza.cook()是哪一種pizza我不用知道 我只要保證各個Pizza的subclass有實作prepare和cook就可以
Polymorphism Rocks!
但只要有新的Pizza推出或是有舊的要拿掉 需要改這裡的if else 有點麻煩
簡單工廠模式
別忘了Design Pattern rule #1: Encapsulate what varies
把剛剛orderPizza裡面的所有if-else拉出來到一個工廠裡 這個工廠專門製作pizza
就這麼簡單 這就是簡單工廠
簡單工廠管理物件的創造 如果client要取得物件 只要給簡單工廠正確的參數就可以
然後pizza店的constructor要丟一個工廠進去
簡單工廠模式 讓我們把pizza的創造和pizza的使用分開了 減少了client對於實作的依賴
我們成功的判斷出orderPizza這個函數裡面會變動的部分 分離出一個工廠 去處理他 如果你今天要改變處理方式 你去改那個工廠或是給我一個新工廠就可以 我不管你怎麼創造的 我只在乎你回傳給我的object的class是Pizza的subclass
簡單工廠模式的優點就是分離了物件的使用和創造 client不管你怎麼生成的 但缺點也很明確 每當有新的class出來工廠就要改 複雜度上升得很快 適用情況是需要創建的種類比較少 而且客戶對於怎麼創建對象的方法不關心
生意不錯 開個分店
實作了簡單工廠模式之後 同樣是cheese pizza, 紐約跟芝加哥的做法就完全不一樣 我們可以修改SimplePizzaFactory讓createPizza多吃一個參數style
好啦我們都那麼熟了 再演就不像了 我們在這裡應該用繼承
ChicagoPizzaFactory跟NYPizzaFactory都繼承自SimplePizzaFactory 現在我們需要為不同style的PizzaStore建立不同的工廠
如果今天我要點NY風味的起司pizza
明天我想點芝加哥風味的起司pizza
看似沒啥問題 我們現在其實進入了見山不是山見水不是水的境界
為什麼這麼說呢 我們為了decouple物件的創造和物件的使用 製造了一個工廠 可是為了reuse工廠的code 我們使用了繼承
現在我的SimplePizzaFactory其實只是一個介面 我定義了所有繼承了我的class應該要做什麼事(返回customized defined pizza) 真正實際創造物件的地方是子類別的實體工廠
這兩種功能(decouple + hierarchy)同時需要的時候 我們就可以用上今天的主角
工廠方法模式
工廠方法模式定義了一個建立物件的介面 但由子類決定要實例化的類別為何 工廠方法讓類別把 實例化 的動作推遲到了子類
我們現在把createPizza拉回來PizzaStore裡 讓子類別來決定怎麼createPizza
createPizza是抽象方法 留給子類別繼承
讓NYPizzaStore去繼承PizzaStore, 實作createPizza
原本我物件的建立 交給一個外來的工廠處理 現在我把它交給我的子類別處理 而且父類別還可以call子類別實作的函數 這種會互call的function通常依賴性都很高 但我們利用工廠模式讓父類別跟子類別的依賴鬆綁(decouple)了
套用了工廠方法模式之後 怎麼點pizza呢
輕鬆
結構
-
Product(Pizza): 定義factoryMethod(createPizza)所造物件的介面
-
ConcreteProduct(NYStyleCheesePizza): 實作Product
-
Creator(PizzaStore): 宣告factoryMethod(必須傳回Product) 和其他client可以call的API
-
ConcreteCreator(NYPizzaStore): 實作factoryMethod 回傳ConcreteProduct的instance
有個小細節 其實工廠方法不一定是abstract 也可以Creator就先偷偷實作factoryMethod 回傳Product, subclass可以選擇要不要override工廠方法
優缺點
優點除了跟簡單工廠一樣 隱藏了創建物件的細節 最重要的是加入新產品不需要改動Creator 你直接繼承Creator就好了 client的用法都是一樣不需要改 完全符合開放封閉守則
缺點就是ConcreteCreator跟ConcreteProduct會成對的增加 比如你今天想做加州披薩 你在定義完加州pizza之後 還要再定義一個加州pizza工廠
下一篇進入Design Pattern重頭戲 抽象工廠