工廠模式 Factory Pattern

Max Chiang
6 min readOct 23, 2022

--

不為什麼,就是想放

設計模式有分為 創建型模式(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 的例子來說,將風味定義成一個系列,分別建立成夏威夷、墨西哥風味的配料工廠,後續再組合成想要的成品。所以如果你想要建立一系列相關物件的話,你就可以考慮使用抽象工廠模式

參考資料

深入淺出設計模式

--

--