更多請移步: 我的博客
目的
Abstract Factory是創建模式的一種,讓你在沒有指定具體類的情況下生產相關對象的系列。
問題
假設你在寫一個傢俱店的模擬器。你的代碼由以下構成:
相關產品的系列,像:Chair + Sofa + CoffeeTable。
系列的幾個變種。比如,產品Chair + Sofa + Coffee + CoffeeTable可以有這些變種:IKEA,VictorianStyle,ArtDeco。
你需要有種方式創建個性化的傢俱對象,以便它們能夠匹配相同系列中的其他對象。在沒有匹配到傢俱時,客戶會感到失望。
另外,你不想在添加新產品或者產品系列時改變已經存在的代碼。傢俱供應商常常會更新他們的目錄,並且你不想每次在供貨商改目錄時修改你的核心代碼。
解決
第一,Abstract Factory模式建議遍歷所有不同的產品並且強制這些變種遵循通用的接口。比如,所有的椅子變種必須遵循Chair接口;所有的咖啡桌必須遵循CoffeeTable接口。
第二步,創建AbstractFactory,一個基礎接口,聲明瞭創建產品系列的所有產品的方法(比如:createChair,createSofa和createCoffeeTable)。這步最重要的一件事就是讓這些方法返回代表抽象產品類型的接口:Chair,Sofa,CoffeeTable。
第三步,實現具體的工廠。工廠是返回某種特定產品的類。比如,IKEAFactory,將只會返回IKEAChair,IKEASofa和IKEACoffeeTable對象。所有的工廠在創建同一個變種的產品時必須遵循AbstractFactory接口。
客戶端代碼只能通過抽象接口同工廠和產品協作。這樣你就可以通過傳遞不同的工廠對象修改要用的產品類型。
所以,當客戶端像工廠請求創建一把椅子時,它不必關心工廠的具體類。也不必關心它將得到椅子的具體類。不管它將得到一把時尚的IKEA模型還是一把Victorian風格的椅子,它都用使用抽象的Chair接口和椅子協作。客戶端代碼只需要知道椅子實現了在接口中聲明的sit方法。它還知道不管返回哪種椅子,它的類型是和沙發還有咖啡桌匹配的,因爲它們是同一個工廠創建的。
好了,但是誰創建實際的工廠對象呢?通常,程序在初始化階段創建一個具體的工廠,並且工廠的類型依賴配置或者環境。
結構
Abstract products爲創建產品系列的所有不同產品聲明接口。通常,有幾個不同的產品接口。
Concrete products實現不同Abstract product接口。實現相同接口的具體產品集合代表一個系列的不同變種。
Abstract factory聲明瞭創建系列中所有產品的接口。
Concrete factory實現了abstract factory的創建方法。每個具體工廠代表一個系列產品的特定變種。
雖然具體的工廠實例化具體的產品,但是創建方法的簽名必須聲明爲相應抽象產品類型。
通過這種歌方式,客戶端代碼在使用工廠時就不會和具體的產品變種耦合。它就能通過使用抽象接口和任何工廠/產品協作。
僞代碼
這個例子用來說明Abstract Factory模式可一用來創建跨平臺的UI而不需要客戶端代碼和具體UI類耦合。
客戶端代碼從工廠中請求各個UI元素。返回元素的具體類型取決於客戶端代碼傳遞的工廠類型。客戶端代碼通過抽象接口和元素協作。只要它使用相同的工廠對象,它所有的產品都是兼容的。
Abstract Factory模式使得客戶端代碼和具體UI元素類獨立。另外,當添加一個新的UI變種時,你不需要修改已經存在的代碼(比如,實現Linux的UI元素)。你只需要創建一個工廠的新子類,讓他返回新類型的UI元素。
// This pattern assumes that you have several families of products, structured
// into separate class hierarchies (Button/Checkbox). All products of the same
// family have the common interface.
interface Button is
method paint()
// All products families have the same varieties (macOS/Windows).
class WinButton implementing Button is
method paint() is
Render a button in a Windows style
class MacButton implementing Button is
method paint() is
Render a button in a Mac OS X style
interface Checkbox is
method paint()
class WinCheckbox implementing Checkbox is
method paint() is
Render a checkbox in a Windows style
class MacCheckbox implementing Checkbox is
method paint() is
Render a checkbox in a Mac OS X style
// Abstract factory knows about all (abstract) product types.
interface GUIFactory is
method createButton():Button
method createCheckbox():Checkbox
// Each concrete factory extends basic factory and responsible for creating
// products of a single variety.
class WinFactory implementing GUIFactory is
method createButton():Button is
return new WinButton
method createCheckbox():Checkbox is
return new WinCheckbox
// Although concrete factories create the concrete products, they still return
// them with the abstract type. This fact makes factories interchangeable.
class MacFactory implementing GUIFactory is
method createButton():Button is
return new MacButton
method createCheckbox():Checkbox is
return new MacCheckbox
// Factory users don't care which concrete factory they use since they work with
// factories and products through abstract interfaces.
class Application is
private field button: Button;
constructor Application(factory: GUIFactory) is
this.factory = factory
method createUI();
this.button = factory.createButton()
method paint();
button.paint()
// Application picks the factory type and creates it in run time (usually at
// initialization stage), depending on the configuration or
// environment variables.
class ApplicationConfigurator is
method main() is
Read the configuration file
If the OS specified in the configuration file is Windows, then
Construct a WinFactory
Construct an Application with WinFactory
else
Construct an MacFactory
Construct an Application with MacFactory
適用性
當業務邏輯必須要和同一個產品系列的不同變種協作,並且你不想依賴具體產品類時(或者它們無法預先知道)。
Abstract Factory對客戶端代碼隱藏了創建產品類的信息。客戶端代碼可以和任何工廠創建出來的任何產品協作,只要客戶端通過抽象接口和它們交互。
當一個類擁有多個Factory Method使得它的主要責任不明確時。
每個類專注做一件事是很好的程序設計。當一個類需要處理多個產品類型,它就應該使用一個獨立的抽象工廠來替代多個工廠方法。
如何實現
1. 畫出不同產品和相同產品不同變種的矩陣。
2. 爲所有不同產品類型創建抽象接口並且讓所有具體產品遵循這些接口。
3. 聲明抽象工廠接口。這個接口應該列出所有不同類型產品的創建方法。
4. 爲產品系列的每個變種實現不同的工廠類。
5. 創建一個空場的初始化代碼寫在客戶端中。客戶端依賴配置或者當前環境中來決定需要的工廠的類型並創建出來。
6. 在客戶端代碼中,把所有調用產品構造方法的地方替換成調用工廠的創建方法。
優點
符合開閉原則。
允許構建系列產品對象並擔保兼容性。
避免具體產品和使用它們的代碼的耦合。
在多個類間隔離責任。
缺點
- 創建多個額外類,增加代碼整體複雜度。
和其他模式的關係
通常,設計從使用Factory Method開始(比較簡單,並且可以通過子類實現定製),逐漸演變到Abstract Factory,Prototype,或者Builder(更加複雜,但更靈活),因爲設計者發現它們需要更靈活的程序。
Builder關注點在一步一步的構造出一個複雜對象。Abstract Factory創建產品對象的系列(不管是簡單的還是複雜的)。Builder在最後一步返回產品,但是Abstrct Factory立刻返回結果。
Abstract Factory類通常用Factory Method來實現,單絲它們也可以用Prototype來實現。
Abstract Factory可以用來代替Facade隱藏特定平臺的類。
Abstract Factory可以和Bridge模式單獨使用。當Bridge的“接口”部分只能與特定的“實現”一起工作時非常有用。這種情況下,工廠能夠封裝這些關係並且對客戶端隱藏複雜性。
Abstract Factory,Builder和Prototype都可以實現爲Singleton。
小結
Abstract Factory是創建型模式的一種,用來解決創建沒有制定具體類的產品系列。
Abstract Factory定義了一個創建所有不同產品的接口,但是把創建真正產品的實現放在具體工廠類中。美國工廠類型都代表一個某些產品的變種。
客戶端代碼不是直接調用構造方法(new操作)而是調用一個工廠對象的創建方法來創建對象。因爲一個工廠代表了一個產品的變種,它的產品都是兼容的。
客戶端只通過抽象接口和工廠還有產品協作。它允許相同的客戶端代碼和不同的產品協作。你只需要創建一新的具體工廠類,並把它傳遞給客戶端代碼就行。如果你分不清楚Factories,Factory Method和Abstract Factory,可以閱讀工廠比較指南(待譯)。
Java的模式的使用
用例:許多框架和類庫採用抽象工廠模式來提供擴展和定製它們的標準組件。
Java的核心包中應用如下:
javax.xml.parsers.DocumentBuilderFactory#newInstance()
javax.xml.transform.TransformerFactory#newInstance()
javax.xml.xpath.XPathFactory#newInstance()
鑑定:該模式很容易通過返回工廠對象的方法來識別。然後,工廠用於創建特定的子組件。
參考
翻譯整理自:https://refactoring.guru/design-patterns/abstract-factory