天天看點

設計模式之AbstractFactory(抽象工廠)

抽象工廠模式(Abstract Factory Pattern)

引入:

在前面介紹的兩個建立型模式裡面,我們解決的都是有關"new"的問題,用它們來避免顯式指定類建立對象。我寫的也非常簡單易懂,相信看過的朋友們都應該對簡單工廠模式、工廠方法模式的意圖、所能解決的問題及适用情景有一定的了解了。但是若要達到靈活運用,什麼時候用,怎樣用合适還不是看一篇文章就能解決的問題。呵呵..這需要你對OO的了解程度,你的項目開發經驗等等許多方面的積累。一起努力喔。。

好了,咱們言歸正傳,通過對這兩個模式的了解,我們掌握一種思想,就是在建立一個對象時,需要把容易發生變化的地方給封裝起來,來控制變化(哪裡變化,封裝哪裡),以适應客戶的變動,項目的擴充。但是,我們在軟體系統中,經常面臨着“一系列互相依賴的對象”的建立工作,同時由于需求的變化,這“一系列互相依賴的對象”也要改變,如何應對這種變化呢?如何像簡單工廠模式、工廠方法模式一樣繞過正常的"new",然後提供一種“封裝機制”來避免客戶程式和這種“多系列具體對象建立工作”的緊耦合?可能有人會說,你也可以将這些對象一個一個通過工廠方法模式來解決呀?但是,我們試想,既然是一系列互相依賴的對象,它們是有聯系的,每個對象都這樣解決,你又如何來保證他們的聯系呢?舉一個例子:Windows桌面主題,當你更換一個桌面主題的時候,系統的開始按鈕、工作列、菜單欄、工具欄等等都變了,而且是一起變的,他們的色調都還很一緻,難道類似這樣的問題,怎麼來解決呢?它的天敵就是抽象工廠模式。

意圖:

提供一個建立一系列相關或互相依賴對象的接口,而無需指定它們具體的類。

參考者:

也就是該模式中的各個類或對象之間的關系:

  • 抽象工廠(Abstract Factory)

    聲明生成一系列抽象産品的方法

  • 具體工廠(Concrete Factory)

    執行生成一系列抽象産品的方法,生成一系列具體的産品

  • 抽象産品(Abstract Product)

    為這一系列的某一種産品聲明接口

  • 具體産品(Product)

    定義具體工廠生成的具體産品的對象,實作産品接口

  • 客戶(Client)

    我們的應用程式用戶端(不要了解成人),使用抽象産品和抽象工廠生成對象。

 抽象工廠模式UML圖

設計模式之AbstractFactory(抽象工廠)

抽象工廠模式在生活中的執行個體

咱們繼續拿怎麼穿衣服來說明這個抽象工廠模式。

就拿你來說吧。工作了,為了參加一些聚會,肯定有兩套或多套衣服吧,比如說有商務裝(成套,一系列具體産品)、時尚裝(成套,一系列具體産品),甚至對于一個家庭來說,可能有商務女裝、商務男裝、時尚女裝、時尚男裝,這些也都是成套的,即一系列具體産品。咱們假設一種情況(現實中是不存在的,要不然,沒法進入共産主義了,但有利于說明抽象工廠模式),在你的家中,某一個衣櫃(具體工廠)隻能存放某一種這樣的衣服(成套,一系列具體産品),每次拿這種成套的衣服時也自然要從這個衣櫃中取出了。用OO的思想去了解,所有的衣櫃(具體工廠)都是衣櫃類的(抽象工廠)某一個,而每一件成套的衣服又包括具體的上衣(某一具體産品),褲子(某一具體産品),這些具體的上衣其實也都是上衣(抽象産品),具體的褲子也都是褲子(另一個抽象産品)。

分析:

要好好去讀上面那個執行個體,雖然有點繞嘴,其實隻要用心去讀,厘清了抽象工廠模式的各個角色,對了解設計模式是非常重要的。了解頭緒,然後接合簡單工廠模式、工廠方法模式對工廠家族的了解,再加上抽象工廠模式的意圖,頭腦中差不多有一個雛型了吧。好了,咱們一起來分析一下。。

先把各個角色揪出來。

抽象工廠:虛拟的衣櫃,它隻是個概念而已。在項目中可能是一個接口或抽象類,定義規則,取出上衣,褲子。

具體工廠:具體的存在的衣櫃,它用于存放某一種成套的衣服,換句話說,這種成套的衣服都是從這個衣櫃中取出的。在項

              目中繼承于抽象工廠,實作抽象工廠中的方法,取出具體産品,某一件上衣,某一條褲子。

抽象産品:虛拟的衣服,也隻是個概念。在項目中可能是多個接口或抽象類,定義規則,有什麼特性,起什麼作用。

具體産品:具體的實際存在的産品,它指的就是用于組裝成某一套衣服的某一件上衣或褲子。它繼承自某一個抽象産品。實

              現抽象産品中制定的規則,特性。

它們之間怎麼聯系呢?客戶在用的時候,依賴的又是什麼呢?

客戶在要的時候,首先要說出你要的什麼系列的衣服,然後根據它的要求生成一個具體工廠的執行個體,剩下的工作就都是這個倒黴的具體工廠了,它會根據自己的實作生成一個上衣,生成一個褲子,然後把它交給客戶。客戶在這一過程中并不知道具體工廠都做了什麼。也就是說,客戶隻依賴于抽象工廠和抽象産品了。在初始化的時候會用到一次具體工廠類名,我們根據.NET特有的反射機制又可以把這個在用戶端唯一的具體的非抽象類放到一個應用程式配置檔案中,防止它變化。

這就符合了設計模式中的“開放--封閉”原則,依賴倒轉原則,裡氏代換原則等等。

具體代碼如下:

抽象工廠角色:

 namespace AbstractFactory

{

//抽象工廠類,

public abstract class AbstractClothes

{

//抽象方法:建立一個上衣

abstract public AbstractCoat CreateCoat();

//抽象方法:建立一個褲子

abstract public AbstractTrousers CreateTrousers();

}

}

抽象産品角色:

1namespace AbstractFactory

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

具體工廠角色:

1 namespace AbstractFactory

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 }

具體産品角色:(注意:我并沒有把所有的具體産品類都列出來,由于簡單,可以推想出剩餘的産品類,詳見附件)

  1namespace AbstractFactory

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}

用戶端代碼:

1namespace AbstractFactory

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檔案

1<configuration>

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

©2007 renly

原創作品,歡迎轉載,轉載請注明來自部落格園,留此資訊。

繼續閱讀