本章介紹 FACTORY 模式,說明如何在遵守 DIP(依賴反轉原則)的前提下建立具體類別的實例。FACTORY 模式讓高層策略模組不需要知道具體實作類別,同時保持建立物件的能力。

問題:DIP 與物件建立的矛盾#

  • DIP 告訴我們高層模組不應依賴低層模組,兩者都應依賴抽象
  • 但建立物件時,我們必須呼叫具體類別的建構子——這直接違反了 DIP
  • FACTORY 模式就是為了解決這個矛盾:將物件建立的邏輯隔離到一個獨立的類別中

FACTORY 的基本結構#

Figure 29.2: Application of FACTORY to SomeApp

  • 定義一個抽象的 Factory 介面,宣告建立物件的方法(回傳抽象型別)
  • 具體的 Factory 實作類別負責 new 出具體物件
  • 應用程式依賴抽象的 Factory 介面,而非具體的 Factory 實作
public interface ShapeFactory
{
    Shape MakeSquare();
    Shape MakeCircle();
}

public class ShapeFactoryImplementation : ShapeFactory
{
    public Shape MakeSquare() { return new Square(); }
    public Shape MakeCircle() { return new Circle(); }
}

可替換的工廠(Substitutable Factories)#

Figure 29.3: Substitutable factory

  • 由於應用程式只依賴 Factory 介面,可以在不同情境下替換不同的 Factory 實作
  • 典型場景:測試時使用測試用的 Factory,正式環境使用真實的 Factory
  • 這讓整個系統的具體實作可以在啟動時(或測試時)一次性替換

在薪資系統中的應用#

Figure 29.4: Payroll uses database

  • 薪資系統的 TransactionApplication 需要使用 PayrollDatabase,但不應直接依賴 SqlPayrollDatabase
  • 解法:建立 PayrollDatabase 介面與對應的 PayrollDatabaseFactory

Figure 29.6: Spoofing the factory

  • 測試時可以用假的 Factory(Spoof) 來替換真實的資料庫實作,注入一個記憶體版本的 PayrollDatabase
  • 這避免了測試對真實資料庫的依賴,大幅加速測試執行

重點: FACTORY 模式的核心價值不僅是封裝建立邏輯,更是維護 DIP 的完整性。透過 FACTORY,高層模組可以在完全不知道具體類別的情況下建立物件。

初始化問題#

  • 如果所有具體實作都隱藏在 Factory 後面,那麼誰來建立 Factory 本身?
  • 答案是系統的 main 函式或啟動程序:它是整個系統中唯一「知道所有具體類別」的地方
  • main 建立具體的 Factory 實作,並將其注入到需要的模組中

技巧: FACTORY 模式通常與 SINGLETON 或靜態方法搭配使用,讓 Factory 的實例在全系統中可存取。但要注意不要讓這種全域存取點破壞了可測試性。

本章小結#

FACTORY 模式是 DIP 的必要搭配——它讓我們在遵守「依賴抽象」的原則下,仍能建立具體物件。透過可替換的 Factory,系統的具體實作可以在測試與正式環境之間無縫切換。