抽象工廠
抽象工廠設計模式是一種創建模式,它提供了一個用于創建相關或從屬對象族的接口,而無需指定其具體類。
它在以下情況下特別有用:
您需要創建必須一起使用并且是一致系列的一部分的對象(例如,按鈕、復選框和菜單等 GUI 元素)。
您的系統必須支持多種配置、環境或產品變體(例如,淺色與深色主題、Windows 與 macOS 外觀)。
您希望在相關對象之間強制保持一致性,確保它們都是從同一個工廠創建的。
抽象工廠模式將 對象創建封裝到工廠接口中。
每個具體工廠實現接口并生成一組完整的相關對象。這可確保您的代碼保持可擴展、一致并與特定產品實現松散耦合。
讓我們通過一個真實世界的示例,看看如何應用抽象工廠模式來構建一個靈活、可維護的系統,并且能夠在沒有條件邏輯的情況下支持多個可互換的產品系列。
問題:特定于平臺的 UI
假設你正在構建一個必須同時支持 Windows 和 macOS 的跨平臺桌面應用程序。
為了提供良好的用戶體驗,您的應用程序應 為每個作系統呈現具有本機外觀的 UI 組件,例如:
按鈕
復選框
文本字段
菜單
樸素實現:條件 UI 組件實例化
在第一次嘗試中,您可以實現特定于平臺的 UI 組件,如下所示:
Windows UI 元素
??public?class?WindowsButton?implements?Button?{@Overridepublic?void?paint()?{System.out.println("Painting?a?Windows-style?button.");}@Overridepublic?void?onClick()?{System.out.println("Windows?button?clicked.");}
}public?class?WindowsCheckbox?implements?Checkbox?{@Overridepublic?void?paint()?{System.out.println("Painting?a?Windows-style?checkbox.");}@Overridepublic?void?onSelect()?{System.out.println("Windows?checkbox?selected.");}
}
MacOS UI元素
public?class?MacOSButton?implements?Button?{@Overridepublic?void?paint()?{System.out.println("Painting?a?macOS-style?button.");}@Overridepublic?void?onClick()?{System.out.println("MacOS?button?clicked.");}
}public?class?MacOSCheckbox?implements?Checkbox?{@Overridepublic?void?paint()?{System.out.println("Painting?a?macOS-style?checkbox.");}@Overridepublic?void?onSelect()?{System.out.println("MacOS?checkbox?selected.");}
}
然后,在應用程序邏輯中,您最終會執行以下作:
public?class?App?{public?static?void?main(String[]?args)?{String?os?=?System.getProperty("os.name");if?(os.contains("Windows"))?{WindowsButton?button?=?new?WindowsButton();WindowsCheckbox?checkbox?=?new?WindowsCheckbox();button.paint();checkbox.paint();}?else?if?(os.contains("Mac"))?{MacOSButton?button?=?new?MacOSButton();MacOSCheckbox?checkbox?=?new?MacOSCheckbox();button.paint();checkbox.paint();}}
}
為什么這種方法會失敗
雖然此設置適用于兩個平臺上的兩個 UI 組件,但隨著應用的增長,它很快就會成為一場噩夢。
- 1. 與混凝土類緊密耦合
您的主應用程序邏輯與特定于平臺的類 (, , 等) 緊密綁定。這意味著無論在何處創建 UI 組件,都必須手動檢查作系統。WindowsButtonMacOSCheckbox - 2. 無抽象或多態性
不能籠統地處理按鈕或復選框。沒有像或可以使用的通用界面 。ButtonCheckbox - 3. 代碼重復和重復
每個特定于平臺的塊都復制了類似的邏輯——實例化按鈕、復選框、菜單等。你將在整個代碼庫中重復此條件分支。 - 4. 可擴展性問題
當您:
添加新平臺(例如 Linux)?
添加新組件(例如,、、)?TextFieldSliderMenuBar
您必須:
為每個平臺添加新的具體類
修改代碼中出現特定于平臺的邏輯的每個位置
破壞現有行為的風險
- 5. 違反開放/關閉原則
任何新的變體都需要修改現有代碼。您的 UI 創建邏輯不開放擴展,但對重大更改非常開放。
我們真正需要什么
我們需要一種方法來:
按平臺對相關組件(按鈕、復選框等)進行分組
將特定于平臺的創建邏輯封裝到工廠中
以多態方式處理 UI 組件,無論平臺如何
輕松添加新的 UI 元素系列,而無需修改應用程序的核心邏輯
這就是抽象工廠模式的用武之地。
抽象工廠模式
抽象工廠模式提供了一個接口,用于創建相關或依賴對象的族,而無需指定其具體類。
在我們的例子中,我們想要創建一個 UI 組件系列(如 、 、 等),這些組件在不同平臺(例如 Windows 或 macOS)上的外觀和行為不同,但向應用程序公開相同的界面。ButtonCheckboxTextField
抽象工廠模式通過以下方式幫助我們實現這一目標:
定義抽象 UI 工廠接口(例如 GUIFactory)
為每個平臺實施一個具體工廠(例如, WindowsFactoryMacOSFactory)
為每種類型的組件公開一組一致的接口(例如,, ButtonCheckbox)
讓客戶端使用這些接口,而無需擔心特定于平臺的實現
類圖
?
- 1. 抽象工廠 (GUIFactory)
定義用于 創建相關產品系列的通用接口。
通常包括工廠方法,如 、 、 等。createButton()createCheckbox()createTextField()
客戶端依靠此接口創建對象,而無需知道其具體類型。
- 2. 混凝土廠 (, WindowsFactoryMacOSFactory)
實現抽象工廠接口。
創建 屬于特定系列或平臺的具體產品變型。
每個工廠都確保其生產的所有組件都是兼容的(即屬于同一平臺/主題)。
- 3. 抽象產品 (, ButtonCheckbox)
為 一組相關組件定義接口或抽象類。
給定類型(例如,,)的所有產品變體都 將實現這些接口。WindowsButtonMacOSButton
- 4. 混凝土產品 (, WindowsButtonMacOSCheckbox)
實現抽象產品接口。
包含 組件的特定于平臺的邏輯和外觀。
- 5. 客戶 (Application)
使用抽象工廠和抽象產品接口。
完全不知道它正在使用的具體類——它只與工廠和產品接互。
可以通過更改出廠方式切換整個產品系列(例如,從 Windows 切換到 macOS),而無需接觸 UI 邏輯。
實現抽象工廠
讓我們實現一個系統,讓我們的應用程序可以為 Windows 和 macOS 生成具有本機外觀的 UI 組件(按鈕、復選框), 而無需硬編碼平臺檢查或復制邏輯。
- 1. 定義抽象產品接口
我們首先定義客戶端使用的產品接口。
Button
public?interface?Button?{void?paint();void?onClick();
}
Checkbox
public?interface?Checkbox?{void?paint();void?onSelect();
}
- 2. 創建具體產品類
這些實現特定于平臺的邏輯。
Windows 組件
public?class?WindowsButton?implements?Button?{@Overridepublic?void?paint()?{System.out.println("Painting?a?Windows-style?button.");}@Overridepublic?void?onClick()?{System.out.println("Windows?button?clicked.");}
}public?class?WindowsCheckbox?implements?Checkbox?{@Overridepublic?void?paint()?{System.out.println("Painting?a?Windows-style?checkbox.");}@Overridepublic?void?onSelect()?{System.out.println("Windows?checkbox?selected.");}
}
macOS 組件
public?class?WindowsCheckbox?implements?Checkbox?{@Overridepublic?void?paint()?{System.out.println("Painting?a?Windows-style?checkbox.");}@Overridepublic?void?onSelect()?{System.out.println("Windows?checkbox?selected.");}
}public?class?MacOSCheckbox?implements?Checkbox?{@Overridepublic?void?paint()?{System.out.println("Painting?a?macOS-style?checkbox.");}@Overridepublic?void?onSelect()?{System.out.println("MacOS?checkbox?selected.");}
}
- 3. 定義抽象工廠
這是用于創建相關產品系列的界面。
public?interface?GUIFactory?{Button?createButton();Checkbox?createCheckbox();
}
- 4. 實施混凝土工廠
每個工廠都會創建特定于平臺的組件變體。
WindowsFactory
public?class?WindowsFactory?implements?GUIFactory?{@Overridepublic?Button?createButton()?{return?new?WindowsButton();}@Overridepublic?Checkbox?createCheckbox()?{return?new?WindowsCheckbox();}
}
MacOSFactory
public?class?MacOSFactory?implements?GUIFactory?{@Overridepublic?Button?createButton()?{return?new?MacOSButton();}@Overridepublic?Checkbox?createCheckbox()?{return?new?MacOSCheckbox();}
}
- 5. 客戶端代碼 – 僅使用抽象接口
客戶端代碼根據作系統選擇工廠,并使用它來創建 UI 組件。
public?class?Application?{private?final?Button?button;private?final?Checkbox?checkbox;public?Application(GUIFactory?factory)?{this.button?=?factory.createButton();this.checkbox?=?factory.createCheckbox();}public?void?renderUI()?{button.paint();checkbox.paint();}
}
- 6. 申請切入點
輸出(在 macOS 上)
Painting?a?macOS-style?button.
Painting?a?macOS-style?checkbox.
輸出(在 Windows 上)
Painting?a?Windows-style?button.
Painting?a?Windows-style?checkbox.
我們取得了什么成就
平臺獨立性:應用程序代碼從不引用特定于平臺的類
一致性:按鈕和復選框始終與所選作系統樣式匹配
開放/封閉原則:在不修改現有工廠或組件的情況下添加對 Linux 或 Android 的支持
可測試性和靈活性:工廠可以輕松更換以進行測試或主題
其他資料:
https://pan.baidu.com/s/1c1oQItiA7nZxz8Rnl3STpw?pwd=yftc
https://pan.quark.cn/s/dec9e4868381