Design Patterns – Factory Method


假設我們要開一家Pizza Store, 這間店生產很多種類的Pizza, 有Veggie Pizza, Cheese Pizza還有Clam Pizza, 我們可以怎麼做?

如下圖所示(請按右鍵點新分頁開啟), 如果我們少了SimplePizzaFactory, 每一次在增加Pizza的種類時, 都要再回頭修改PizzaStore的implementation. 這實在是挺麻煩的, 所以我們將createPizza這個method抽出來到SimplePizzaFactory當中, 當客戶執行PizzaStore store.orderPizza(type)時, 在這個function內再去呼叫factory.createPizza(type), 真正生產Pizza的code在SimplePizzaFactory裡面, 這樣子把變動的部份放到另一個class當中, PizzaStore就跟Pizza的種類鬆綁了. 這就是基本Factory的概念, 但是要特別強調的是, 它並不是Design Patterns中的Factory Method, 它只是程式開發當中常常被使用的一種手法, 江湖名號是Simple Factory.

 

接下來的問題來了。如果現在有很多地區的店想要加盟PizzaStore, 例如NYPizzaStore, LAPizzaStore, 他們都想生產相同種類的Pizza, 例如都有Veggie Pizza, Cheese Pizza等等, 不過不同地區因為客人喜好不同, 可能在準備材料(prepare), 烘烤(bake), 切片(cut), 甚至裝盒(box)都有些許不同時該怎麼辦?很直覺的想到就是在Pizza類別之下要有NYStyleVeggiePizza, LAStyleVeggiePizza做繼承跟override的動作, 來區分地區性的些許不同. 那PizzaStore呢?我們是不是也要分地區呢?

 

如上圖(請按右鍵點新分頁開啟), 我們以Veggie Pizza為例, 從Pizza class繼承了NYStyleVeggiePizza以及LAStyleVeggiePizza. 各自都override了prepare(), bake(), cut(), box()等member function. 接著, 跟Simple Factory不同的是, 我們又將createPizza()這個function拉回來到PizzaStore的class內. 只不過這一次, PizzaStore只宣告了abstract的createPizza(), 真正的implement是放在它的subclasses, 即是NYPizzaStore, LAPizzaStore. 而客戶要使用的時候, 則是在實體化PizzaStore object的時候要指定自己要new哪一種的PizzaStore, 是NY的還是LA的?自然而然在createPizza的時候, 就會製作相對應style的pizza.

使用者並沒有在run time指定自己要哪一種地區style的pizza, 而是在一開始new PizzaStore的時候就決定好的了.

在這個例子裡面, createPizza()這個function就被稱為Factory Method. Factory Method定義了建立物件的介面, 而由child class決定要實體化哪個類別. 在這個例子中, PizzaStore是Creator, NYPizzaStore, LAPizzaStore是Concrete Creator, Pizza是Product, NYStyleVeggiePizza, LAStyleVeggiePizza是Concrete Product. Creator並不真正產生Product, 而是由Concrete Creator去負責產生對應的Concrete Product.

Factory Method使用的時機, 依照四人幫的標準定義:

  • 當類別無法明指欲生成的物件類別時
  • 當類別希望讓子類別去指定欲生成的物件類型時
  • 當類別將權力下放給一個或多個輔助用途的子類別, 你又希望將『交付給哪些子類別』的知識集中在一處時