.NET設計模式(3): 抽象工廠模式
抽象工廠模式(Abstract Factory Pattern)
引入:
在前面介紹的兩個創建型模式裏面,我們解決的都是有關"new"的問題,用它們來避免顯式指定類創建對象。我寫的也非常簡單易懂,相信看過的朋友們都應該對簡單工廠模式、工廠方法模式的意圖、所能解決的問題及適用情景有一定的瞭解了。但是若要達到靈活運用,什麼時候用,怎樣用合適還不是看一篇文章就能解決的問題。呵呵..這需要你對OO的理解程度,你的項目開發經驗等等許多方面的積累。一起努力喔。。
好了,咱們言歸正傳,通過對這兩個模式的瞭解,我們掌握一種思想,就是在創建一個對象時,需要把容易發生變化的地方給封裝起來,來控制變化(哪裏變化,封裝哪裏),以適應客戶的變動,項目的擴展。但是,我們在軟件系統中,經常面臨着“一系列相互依賴的對象”的創建工作,同時由於需求的變化,這“一系列相互依賴的對象”也要改變,如何應對這種變化呢?如何像簡單工廠模式、工廠方法模式一樣繞過常規的"new",然後提供一種“封裝機制”來避免客戶程序和這種“多系列具體對象創建工作”的緊耦合?可能有人會說,你也可以將這些對象一個一個通過工廠方法模式來解決呀?但是,我們試想,既然是一系列相互依賴的對象,它們是有聯繫的,每個對象都這樣解決,你又如何來保證他們的聯繫呢?舉一個例子:Windows桌面主題,當你更換一個桌面主題的時候,系統的開始按鈕、任務欄、菜單欄、工具欄等等都變了,而且是一起變的,他們的色調都還很一致,難道類似這樣的問題,怎麼來解決呢?它的天敵就是抽象工廠模式。
意圖:
提供一個創建一系列相關或相互依賴對象的接口,而無需指定它們具體的類。
參考者:
也就是該模式中的各個類或對象之間的關係:
- 抽象工廠(Abstract Factory)
聲明生成一系列抽象產品的方法 - 具體工廠(Concrete Factory)
執行生成一系列抽象產品的方法,生成一系列具體的產品 - 抽象產品(Abstract Product)
爲這一系列的某一種產品聲明接口 - 具體產品(Product)
定義具體工廠生成的具體產品的對象,實現產品接口 - 客戶(Client)
我們的應用程序客戶端(不要理解成人),使用抽象產品和抽象工廠生成對象。
抽象工廠模式UML圖
抽象工廠模式在生活中的實例
咱們繼續拿怎麼穿衣服來說明這個抽象工廠模式。
就拿你來說吧。工作了,爲了參加一些聚會,肯定有兩套或多套衣服吧,比如說有商務裝(成套,一系列具體產品)、時尚裝(成套,一系列具體產品),甚至對於一個家庭來說,可能有商務女裝、商務男裝、時尚女裝、時尚男裝,這些也都是成套的,即一系列具體產品。咱們假設一種情況(現實中是不存在的,要不然,沒法進入共產主義了,但有利於說明抽象工廠模式),在你的家中,某一個衣櫃(具體工廠)只能存放某一種這樣的衣服(成套,一系列具體產品),每次拿這種成套的衣服時也自然要從這個衣櫃中取出了。用OO的思想去理解,所有的衣櫃(具體工廠)都是衣櫃類的(抽象工廠)某一個,而每一件成套的衣服又包括具體的上衣(某一具體產品),褲子(某一具體產品),這些具體的上衣其實也都是上衣(抽象產品),具體的褲子也都是褲子(另一個抽象產品)。
分析:
要好好去讀上面那個實例,雖然有點繞嘴,其實只要用心去讀,分清了抽象工廠模式的各個角色,對理解設計模式是非常重要的。理解頭緒,然後接合簡單工廠模式、工廠方法模式對工廠家族的瞭解,再加上抽象工廠模式的意圖,頭腦中差不多有一個雛型了吧。好了,咱們一起來分析一下。。
先把各個角色揪出來。
抽象工廠:虛擬的衣櫃,它只是個概念而已。在項目中可能是一個接口或抽象類,定義規則,取出上衣,褲子。
具體工廠:具體的存在的衣櫃,它用於存放某一種成套的衣服,換句話說,這種成套的衣服都是從這個衣櫃中取出的。在項
目中繼承於抽象工廠,實現抽象工廠中的方法,取出具體產品,某一件上衣,某一條褲子。
抽象產品:虛擬的衣服,也只是個概念。在項目中可能是多個接口或抽象類,定義規則,有什麼特性,起什麼作用。
具體產品:具體的實際存在的產品,它指的就是用於組裝成某一套衣服的某一件上衣或褲子。它繼承自某一個抽象產品。實
現抽象產品中制定的規則,特性。
它們之間怎麼聯繫呢?客戶在用的時候,依賴的又是什麼呢?
客戶在要的時候,首先要說出你要的什麼系列的衣服,然後根據它的要求生成一個具體工廠的實例,剩下的工作就都是這個倒黴的具體工廠了,它會根據自己的實現生成一個上衣,生成一個褲子,然後把它交給客戶。客戶在這一過程中並不知道具體工廠都做了什麼。也就是說,客戶只依賴於抽象工廠和抽象產品了。在初始化的時候會用到一次具體工廠類名,我們根據.NET特有的反射機制又可以把這個在客戶端唯一的具體的非抽象類放到一個應用程序配置文件中,防止它變化。
這就符合了設計模式中的“開放--封閉”原則,依賴倒轉原則,里氏代換原則等等。
具體代碼如下:
抽象工廠角色:
2{
3 //抽象工廠類,
4 public abstract class AbstractClothes
5 {
6 //抽象方法:創建一個上衣
7 abstract public AbstractCoat CreateCoat();
8 //抽象方法:創建一個褲子
9 abstract public AbstractTrousers CreateTrousers();
10 }
11}
12
抽象產品角色:
2{
3 /// <summary>
4 /// 抽象產品----上衣抽象類
5 /// </summary>
6 public abstract class AbstractCoat
7 {
8 //性別屬性
9 public abstract bool Sex
10 {
11 get;
12 }
13
14 //樣式屬性
15 public abstract string Style
16 {
17 get;
18 }
19 }
20
21 /// <summary>
22 /// 抽象產品----褲子抽象類
23 /// </summary>
24 public abstract class AbstractTrousers
25 {
26 //性別屬性
27 public abstract bool Sex
28 {
29 get;
30 }
31
32 //樣式屬性
33 public abstract string Style
34 {
35 get;
36 }
37 }
38}
39
具體工廠角色:
具體產品角色:(注意:我並沒有把所有的具體產品類都列出來,由於簡單,可以推想出剩餘的產品類,詳見附件)
2{
3 /// <summary>
4 /// 時尚男性上衣
5 /// </summary>
6 public class CoatA:AbstractFactory.AbstractCoat
7 {
8 private bool sex = true;
9 private string style = "時尚";
10 /// <summary>
11 /// 重寫基類的Sex屬性
12 /// </summary>
13 public override bool Sex
14 {
15 get
16 {
17 return sex;
18 }
19 }
20
21 /// <summary>
22 /// 重寫基類的Style屬性
23 /// </summary>
24 public override string Style
25 {
26 get
27 {
28 return style;
29 }
30 }
31 }
32
33 /// <summary>
34 /// 時尚男性褲子
35 /// </summary>
36 public class TrousersA : AbstractTrousers
37 {
38 private bool sex = true;
39 private string style = "時尚";
40 public override bool Sex
41 {
42 get
43 {
44 return sex;
45 }
46 }
47 public override string Style
48 {
49 get
50 {
51 return style;
52 }
53 }
54 }
55}
客戶端代碼:
2{
3 /// <summary>
4 /// 創建衣服類
5 /// </summary>
6 public class CreateClothes
7 {
8 private AbstractCoat myCoat;
9 private AbstractTrousers myTrousers;
10 public CreateClothes(AbstractClothes clothes)
11 {
12 myCoat = clothes.CreateCoat();
13 myTrousers = clothes.CreateTrousers();
14 }
15
16 public void ShowMyClothes()
17 {
18 Console.WriteLine("My Clothes:");
19 string sex= myCoat.Sex ? "男" : "女";
20 //Console.WriteLine("Coat:{0} {1}", myCoat.Sex ? "男" : "女", myCoat.Style);
21 Console.WriteLine("Coat:{0} {1}", sex, myCoat.Style);
22 sex=myTrousers.Sex?"男":"女";
23 Console.WriteLine("Trousers:{0} {1}", sex, myTrousers.Style);
24 }
25 }
26
27
28 public class Client
29 {
30 static void Main(string[] args)
31 {
32 //創建一個工廠類的實例
33 string assemblyName = ConfigurationManager.AppSettings["assemblyName"];
34 string fullTypeName =string.Concat( ConfigurationManager.AppSettings["nameSpaceName"] ,".", ConfigurationManager.AppSettings["typename"]);
35
36 AbstractClothes factory = (AbstractClothes)Assembly.Load(assemblyName).CreateInstance(fullTypeName);
37 CreateClothes clothes = new CreateClothes(factory);
38 clothes.ShowMyClothes();
39 Console.Read();
40 }
41 }
42}
43
app.config文件
2 <appSettings>
3 <!--一般情況下只需改第三個"typename"就行了,具體工廠類 -->
4 <add key="assemblyName" value="ConcreteFactory"/>
5 <add key="nameSpaceName" value="AbstractFactory"/>
6 <add key="typename" value="FashionManClothes"/>
7 </appSettings>
8</configuration>
這樣,代碼就完成了。
小結一下:
抽象工廠模式堪稱gof23種設計模式精典模式之一,它能夠解決諸如:通過顯示指定類創建對象,緊耦合,對對象表示或實現的依賴等等一些問題,有關設計模式的設計原則,所能解決的問題,詳見OO與設計模式的原則、目標 。
抽象工廠模式適用於對“一系列相互依賴的對象”的創建工作,這些對象是相互依賴的,是有聯繫的。如果僅爲一個對象的創建則用簡單工廠模式或工廠方法模式完全可以實現,沒有必要用抽象工廠模式。
由於抽象工廠模式的客戶端只依賴於抽象工廠,抽象產品,在初始化過程中僅用到一次具體工廠我們又把它放在了app.config中了,完全依賴接口,這樣不僅在系統的擴展性方面好,而且可以提高團隊開發效率。兩個團隊只要彼此瞭解定義的接口,抽象類,可以並行開發。舉個例子,就拿博客園來說吧,我們在用自己的博客空間時,可以隨時的換皮膚,這個換皮膚是不是典型的抽象工廠模式嗎?如果是,它的各個角色又是什麼呢?我認爲是的。換一下皮膚,你博客頁面上的各個樣式都變了,而且這裏各個樣式都同屬於你選定的這一個皮膚。而每個樣式都又是獨立的,它們組合起來就成了一款皮膚。我們來揪出來各個角色。
抽象工廠:皮膚
抽象產品:樣式
具體工廠:某一款皮膚,皮膚名即爲具體工廠的類名
具體產品:某一個樣式。
雖然不存在這樣的接口與類,但是它確實是抽象工廠模式的一個應用。抽象工廠制定都有哪些樣式名,而具體工廠來實現這些樣式名中的樣式,而具體工廠中用到的各個樣式都是一個具體產品。這也是我的理解,如兄弟們有不同的見解,歡迎發表意見,共同探討。
確定過各個角色之後,就可以說一下爲什麼提高效率了。不論dudu在設計博客園時用什麼工具或語言,它與瀘江博客只要約定好所有用到的樣式名就可以了。而瀘江博客就可以根據要求單獨去做每一款皮膚了。
優點:
- 隔離了具體類的生成,客戶不需要知道怎樣生成了每一個具體產品,什麼時間生成的。它將客戶與具體的類分離,依賴於抽象類,耦合性低。
- 一個產品族中的多個對象被設計成一起工作,它能夠保證客戶端始終只使用一個產品族中的對象。這對一些需要根據當前環境來決定其行爲的軟件系統來說,是非常實用的一種設計模式。
- 它有利於更換產品系列,由於客戶端只依賴於抽象類,具體類也被寫到應用程序配置文件中,更換產品系列時,只須更改一下具體工廠名就行了。
缺點:
- 難以支持新種類的產品。難以擴展抽象工廠以生產新種類的產品。這是因爲抽象工廠幾口確定了可以被創建的產品集合,支持新種類的產品就需要擴展該工廠接口,這將涉及抽象工廠類及其所有子類的改變。
應用情景:
- 同一個產品族的產品在一起使用時,而且它們之間是相互依賴的,不可分離
- 系統需要由相互關聯的多個對象來構成
- 你想提供一組對象而不顯示它們的實現過程,只顯示它們的接口
- 系統不應當依賴某一些具體產品類。
應用場景舉例:
- 遊戲開發中的多風格系列場景
- 系統更改皮膚
- 支持多種觀感標準的用戶界面工具箱(Kit)。
參考資料
- 《深入淺出設計模式(C#/Java版) 》 清華大學出版社
- MSDN Webcast C#面向對象設計模式縱橫談 李建忠老師
源程序下載:
http://files.cnblogs.com/anlyren/AbstractFactory.rar
2{
3 /// <summary>
4 ///時尚男裝
5 /// </summary>
6 public class FashionManClothes:AbstractClothes
7 {
8 public override AbstractFactory.AbstractCoat CreateCoat()
9 {
10 return new CoatA();
11 }
12
13 public override AbstractTrousers CreateTrousers()
14 {
15 return new TrousersA();
16 }
17 }
18
19 /// <summary>
20 /// 時尚女裝
21 /// </summary>
22 public class FashionWomanClothes : AbstractClothes
23 {
24 public override AbstractCoat CreateCoat()
25 {
26 return new CoatB();
27 //throw new Exception("The method or operation is not implemented.");
28 }
29 public override AbstractTrousers CreateTrousers()
30 {
31 return new TrousersB();
32 //throw new Exception("The method or operation is not implemented.");
33 }
34 }
35}