C#設計模式之創建類模式:抽象工廠模式
定義:提供一個創建一系列相關或相互依賴對象的接口,而無須指定他們具體的類。
概念
要理解抽象工廠模式,首先要了解幾個概念,一個是產品等級結構,另一個是產品族。
在工廠方法模式中引入了工廠等級結構,解決了簡單工廠模式中工廠類職責太重的問題,但由於工廠方法模式中的每個具體工廠只有一個或者一組重載的工廠方法,只能生產一種產品,可能會導致系統中存在大量的工廠類,勢必會增加系統的開銷,有時候可能需要一個工廠能夠提供多種產品對象,而不是單一的產品對象,例如海爾電器公司可以生產海爾電視、海爾冰箱、海爾洗衣機等,公司或工廠可以生產多種產品,而不是單一的某個產品。此時,可以考慮將一些相關的產品組成一個“產品族”,由同一個工廠來統一生產,這就是本章將要學習的抽象工廠模式的基本思想。為了更好的理解抽象工廠模式,先引入以下兩個概念,就是產品等級結構和產品族。
產品等級結構:產品等級結構就是產品的繼承結構,例如一個抽象類是電視機,其子類可以是海爾電視、海信電視、小米電視,則抽象電視機與具體品牌的電視機之間構成了一個產品的等級結構,抽象電視機是父類,而具體品牌的電視機是其子類。
產品族:在抽象工廠模式中,產品族是指由同一個工廠生產的,位於不同產品等級結構中的一組產品。例如海爾電視、海爾冰箱和海爾洗衣機是同一個產品族,這個產品族由三個分屬不同的產品等級結構中,組合成了一個產品族。
產品等級結構和產品族的示意圖如下:
在上圖,不同顏色的多個正方形、圓形和橢圓形分別構成了3個不同的產品等級結構,而顏色相同的正方形,圓形和橢圓構成了一個產品族,每一個形狀對象都位於某個產品族,並屬於某個產品等級結構,上圖共有5個產品族,分屬於3個產品等級結構中。只要指定一個產品所處的產品族和他所屬的等級結構,就可以確定這個唯一的產品。
抽象工廠模式概述
當系統所提供的工廠生產的具體產品並不是一個簡單的額對象,而是多個位於不同產品等級結構、屬於不同類型的具體產品時就可以使用抽象工廠模式。抽象工廠模式時多有形式的工廠模式中最為抽象和最具一般性的一種形式。抽象工廠模式與工廠方法模式最大的區別在於,工廠方法模式針對單一的產品結構,而抽象工廠模式需要面對多個產品等級結構。當一個工廠等級結構可以創建出分屬於不同產品等級結構的一個產品族中的所有對象時,抽象工廠模式比工廠方法模式更為簡單、更有效率。抽象工廠模式的示意圖如下:
上圖中,每一個具體工廠都可以生產一個產品族上面的所有產品,例如顏色相同的正方形、圓形、橢圓。這些產品分屬於三個不同的產品等級結構。如果要用簡單工廠模式來做,只設計一個工廠類,在其中要寫15個創建不同產品的方法,職責過重;如果要用工廠方法模式來寫,要分別寫15個不同的實體工廠以及三個不同的抽象工廠(工廠方法模式是針對單一產品等級結構,所以要寫三個不同的抽象工廠代表每一個產品等級結構),系統中的類的個數成倍增加,系統開銷和復雜度都由上升;目前為止最好的辦法就是使用抽象工廠模式了,只需要創建一個針對不同產品等級結構組成的產品族的一個抽象的工廠類,以及五個分別生產不同產品族的實體工廠。可以看出抽象工廠大大的減少了系統中類的數量。下面列出上面提到的關於抽象工廠模式中的各個組成部分:
①AbstractFactory(抽象工廠):它定義了一組創建一族產品的方法,每一個方法對應一種產品。
②ConcreteFactory(具體工廠):它繼承或實現了抽象工廠,將抽象工廠定義的方法實現為自己特有的行為來生成特定的一族產品類。
③AbstractProduct(抽象產品):它為每種產品聲明接口,聲明了每種產品做具有的業務方法。
④ConcreteProduct(具體產品):定義具體的產品對象,實現或繼承抽象產品中所定義的業務方法。
下圖為抽象工廠模式的類圖:
抽象工廠模式的實現
典型的抽象工廠的代碼
abstract class AbstractFactory { public abstract AbstractProductA CreateProductA(); //工廠方法一 public abstract AbstractProductB CreateProductB(); //工廠方法二 …… }
典型的具體工廠的代碼
class ConcreteFactory1 : AbstractFactory { //工廠方法一 public override AbstractProductA CreateProductA() { return new ConcreteProductA1(); } //工廠方法二 public override AbstractProductB CreateProductB() { return new ConcreteProductB1(); } …… }
抽象工廠的應用實例
實例說明
某軟件公司要開發一套界面皮膚庫,可以對基於.NET平臺的桌面軟件進行界面美化。用戶在使用時可以通過菜單來選擇皮膚,不同的皮膚將提供視覺效果不同的按鈕、文本框、組合框等界面元素,例如春天(Spring)風格的皮膚將提供淺綠色的按鈕、綠色邊框的文本框和綠色邊框的組合框,而夏天(Summer)風格的皮膚則提供淺藍色的按鈕、藍色邊框的文本框和藍色邊框的組合框,其結構示意圖如下圖所示:
該皮膚庫需要具備良好的靈活性和可擴展性,用戶可以自由選擇不同的皮膚,開發人員可以在不修改既有代碼的基礎上增加新的皮膚。
現使用抽象工廠模式來設計該界面皮膚庫。
實例類圖
通過分析,本實例的機構圖如下:
上圖中,SkinFactory充當抽象接口,其子類SpingSkinFactory和SummerSkinFactory充當具體工廠,接口Button、TextField、ComboBox充當抽象產品,其子類SpringButtong、SpingTextField等充當具體產品。
實例代碼
①Button:按鈕接口,充當抽象產品
interface Button { void Display(); }
②SpringButton:SpringButton按鈕類,充當具體產品
class SpringButton : Button { public void Display() { Console.WriteLine("顯示淺綠色按鈕。"); } }
③SummerButton:充當具體產品
class SummerButton : Button { public void Display() { Console.WriteLine("顯示淺藍色按鈕。"); } }
④TextField:文本框接口,充當抽象產品
interface TextField { void Display(); }
⑤SpringTextField:具體產品
class SpringTextField : TextField { public void Display() { Console.WriteLine("顯示綠色邊框文本框。"); } }
⑥SummerTextField:具體類
class SummerTextField : TextField { public void Display() { Console.WriteLine("顯示藍色邊框文本框。"); } }
⑦ComboBox:抽象產品
interface ComboBox { void Display(); }
⑧SpringComboBox:具體產品
class SpringComboBox : ComboBox { public void Display() { Console.WriteLine("顯示綠色邊框組合框。"); } }
⑨SummerComboBox:具體產品
class SummerComboBox : ComboBox { public void Display() { Console.WriteLine("顯示藍色邊框組合框。"); } }
⑩SkinFactory:界面皮膚工廠接口,充當抽象工廠
interface SkinFactory { Button CreateButton(); TextField CreateTextField(); ComboBox CreateComboBox(); }
(11)SpringSkinFactory:具體的皮膚工廠
class SpringSkinFactory : SkinFactory { public Button CreateButton() { return new SpringButton(); } public TextField CreateTextField() { return new SpringTextField(); } public ComboBox CreateComboBox() { return new SpringComboBox(); } }
(12)SummerSkinFactory:具體的皮膚工廠
class SummerSkinFactory : SkinFactory { public Button CreateButton() { return new SummerButton(); } public TextField CreateTextField() { return new SummerTextField(); } public ComboBox CreateComboBox() { return new SummerComboBox(); } }
(13)客戶端調用:
static void Main(string[] args) { //使用抽象層定義 SkinFactory factory; Button bt; TextField tf; ComboBox cb; //讀取配置文件 string factoryType = ConfigurationManager.AppSettings["factory"]; //反射生成對象 factory = (SkinFactory)Assembly.Load("AbstractFactorySample").CreateInstance(factoryType); bt = factory.CreateButton(); tf = factory.CreateTextField(); cb = factory.CreateComboBox(); bt.Display(); tf.Display(); cb.Display(); Console.Read(); }
客戶端調用還是使用配置文件和反射來做一些符合開閉原則的收尾工作。在這個系統中,如果要更換皮膚界面,只需要修改配置文件就可以了。
開閉原則的傾斜性
上面實例其實存在一個很嚴重的問題,那就是如果在設計之初忽略了一些細節,在後面要進行擴展時會非常困難,比如我們現在要增加一個單選按鈕框,由於在抽象工廠類中已經將規則寫死,要想增加單選按鈕框,如果在工廠方法模式中的話直接新增抽象產品類+具體產品類+抽象工廠+具體工廠來對系統進行擴展即可,符合開閉原則,而在抽象工廠模式中是不能這樣做的,因為抽象工廠類中的邏輯已經寫死,在這個例子中,抽象工廠類只定義了Button CreateButton();TextField CreateTextField();ComboBox CreateComboBox();這三個方法,如果新增單選按鈕,那麽必須對源碼進行修改,違反了開閉原則。
在抽象工廠模式中,新增產品族很方便,直接新增一個具體工廠和一族具體產品即可,但是如果新增產品等級結構,則會很麻煩,需要對源碼進行修改,這樣就違反了開閉原則。
抽象工廠模式的優缺點和適用環境
抽象工廠模式是工廠方法模式的進一步的延伸,由於它提供了功能更為強大的工廠類並具備較好的可擴展性,在軟件開發中廣泛的得到了應用,尤其在一些框架和類庫中。它是最常用的設計模式之一。
抽象工廠模式優點
①隔離了具體類的生成,使得客戶端並不需要知道什麽被創建
②當一個產品族中的多個對象被設計成一起工作時,它能夠保證客戶端始終只使用同一個產品族中的對象
③增加新的產品族很方便,無須修改已有系統,符合開閉原則
抽象工廠模式缺點
增加新的產品等級結構麻煩,需要對原有系統進行較大的修改,甚至需要修改抽象層代碼,這顯然會帶來較大的不便,違背了開閉原則
抽象工廠模式適用環境
①一個系統不應當依賴於產品類實例如何被創建、組合和表達的細節
②系統中有多於一個的產品族,但每次只使用其中某一產品族
③屬於同一個產品族的產品將在一起使用,這一約束必須在系統的設計中體現出來
④產品等級結構穩定,設計完成之後,不會向系統中增加新的產品等級結構或者刪除已有的產品等級結構
C#設計模式之創建類模式:抽象工廠模式