工廠模式 Factory Pattern
設計模式有分為 創建型模式(Creational Pattern)
、結構型模式(Structural Pattern)
和行為模式(Behavioral Pattern)
,工廠模式屬於創建型模式
。創建型模式
可以讓創建物件的方法更加彈性靈活,也能提升重用性。
工廠(Factory)
通常用來創建一些函數、 方法或類別。
本篇會依序介紹 簡單工廠模式
、工廠方法模式
和 抽象工廠模式。
舉例:有一家 Pizza 店提供了牛肉披薩跟蔬菜披薩,那該怎麼取得 Pizza 呢?
在取得 Pizza 前,我們先來建立 Pizza 的抽象類別
建立牛肉披薩跟蔬菜披薩
再來就要取得 Pizza 了,如果沒有使用工廠模式的寫法大概會是這樣子:
這段程式有幾個具體類別 (Pizza),需要依照判斷條件(PizzaType)來決定需要實例化哪個對象(牛肉披薩、蔬菜披薩)。getOrderPizza
這個函式包含了創建 Pizza 類別的邏輯跟加工的邏輯(烤、切…),但其實在 getOrderPizza
的方法中, Pizza 的加工方法是不常變動的,而且隨著 Pizza 的品項增加,而需要經常改動這段程式碼。那該怎麼優化呢?
可以用簡單工廠模式來解決,但在實作之前,先來認識一下它吧~
簡單工廠模式(Simple Factory)
簡單工廠其實不算是設計模式,比較像是習慣的寫法。它擁有一個包含大量條件語句的建構函式, 根據傳入的參數來選擇要產生並回傳哪種具體類別。
在上圖getOrderPizza
的方法中, Pizza 的加工方法是不常變動的,但 Pizza 的種類會隨著品項增加,所以可以把建立 Pizza 的邏輯抽離,封裝成一個名叫 SimplePizzaFactory 的類別。
接下來我們可以創建 PizzaStore 來使用工廠為我們創建 Pizza。
這樣子我們就完成了建立 Pizza 跟 Pizza 加工邏輯的拆分。
接下來,當披薩店生意越來越好,要開放其他地區或國家設立加盟店,那該怎麼處理不同地區所提供不同風味的披薩呢?
加盟店分別有夏威夷分店跟墨西哥分店,他們都有賣牛肉批薩跟蔬菜披薩,他們不一樣的地方是夏威夷分店的披薩餅皮是厚皮的,使用到的配料是鳳梨,但是墨西哥分店的披薩餅皮是薄皮的,使用的配料是紅蘿蔔跟辣椒。
假如使用簡單工廠模式
來實作這個需求,需要新增判斷風味的參數,並且要在 createPizza 這個函式中的 when 增加判斷條件,來判斷新的風味,一旦風味越來越多,when 的判斷式會變得很肥大。
要解決上面的問題,那就需要來使用工廠方法模式了,在實作之前,先來認識一下它吧~
工廠方法模式
工廠方法模式定義了一個創建物件的介面,但是他讓子類別決定想要實例化哪一個類別,所以工廠方法其實將實例化類別的動作推遲到子類。
- Creator:抽象類別,包含一個建立物件的抽象方法和操作 Product 的所有方法。
- ConcreteCreator:繼承 Creator 介面並實作建立物件的方法,也就是 ConcreteCreator 來決定想要實例化的類別。
- Product:產品的介面。所有的產品都必須實作同一個介面,這樣才可以讓使用產品的類別引用介面,而不是引用實體類別。
- ConcreteProduct:繼承 Product 介面,並實作 Product。
讓我們來修改一下之前的 Pizza(Product)。因為不同加盟店會有不同的餅皮跟配料,所以我們加上了 dough 跟 toppings 兩個屬性。
建立不同分店的所有種類 Pizza 實作類別(ConcreteProduct)
建立一個抽象類 PizzaStore(Creator ),定義一個抽象的方法 createPizza 讓子類別實作,來生產 Pizza
各個分店( ConcreteCreator),繼承 PizzaStore 並實作 createPizza 方法來生產自己風味的披薩。
文章看到這邊,會發現工廠方法的子類別(HawaiiStylePizzaStore)看起來很像簡單工廠。簡單工廠可以理解為個別的創建作法,而工廠模式則是建立一個框架,並讓子類別決定要如何實作。
接下來,來介紹最後一種抽象工廠模式
抽象工廠模式
抽象工廠模式提供了一個介面來建立相關或相依的物件家族,而不需要指定具體類別。以 Pizza 的範例來說,將風味定義成一個系列,分別建立成夏威夷、墨西哥風味的配料工廠,而這些工廠會分別產生各種風味所需要的配料。
- Client:建立工廠並呼叫 CreateXXXXX 方法,來得到 Product。
- AbstractFactory:抽象工廠介面,這個介面會有一組生產產品的方法。
- ConcreteFactory:抽象工廠介面的實作。
- AbstractProduct:產品抽象介面。每一個 ConcreteFactory 都可以生產一整組的產品。
- ConcreteProduct:繼承 AbstractProduct 介面,並實作 Product。
建立餅皮抽象類別 Dough ,讓餅皮相關的物件來繼承它,例如薄皮或厚皮的物件,又或者是建立蔬菜的抽象類別,讓高麗菜、菠菜或辣椒的物件來繼承它。
建立抽象工廠介面 PizzaIngredientFactory,定義創建 Pizza 所需要的配料(Product),例如餅皮、蔬菜跟牛肉。
建立各種風味的工廠(夏威夷、墨西哥),繼承 PizzaIngredientFactory 並實作內部方法。
在這邊可以發現夏威夷跟墨西哥的工廠,雖然都繼承 PizzaIngredientFactory,但在產生 Pizza 的材料是不一樣的。
因為我們現在 Pizza 製作材料有各種分類(Dough、Beef 和 Veggies),我們修改一下 Pizza 介面
建立具體的 Pizza 時,建構值需要傳入是哪一種風味的工廠 PizzaIngredientFactory,並繼承 Pizza 介面,使用 PizzaIngredientFactory 創建 Product (Dough、Beef …)的方法來取的製作 Pizza 所需要的材料。
這邊有將不同風味 Client 共用的方法都封裝到 PizzaStore 這個抽象類別來讓不同風味 Client 繼承並實作。
工廠方法模式跟抽象工廠模式的差別在於,抽象工廠有把一些屬性抽離出來,可以用這個屬性,建立一系列的相關物件,以 Pizza 的例子來說,將風味定義成一個系列,分別建立成夏威夷、墨西哥風味的配料工廠,後續再組合成想要的成品。所以如果你想要建立一系列相關物件的話,你就可以考慮使用抽象工廠模式
。