程序設計方法學23種設計模式_第1頁
程序設計方法學23種設計模式_第2頁
程序設計方法學23種設計模式_第3頁
程序設計方法學23種設計模式_第4頁
程序設計方法學23種設計模式_第5頁
已閱讀5頁,還剩86頁未讀 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

(1)單例模式

說到單例模式,大家第一反應應該就是——什么是單例模式?,從“單例”字面意思上理解為——一個類只有

一個實例,所以單例模式也就是保證一個類只有一個實例的一種實現方法罷了(設計模式其實就是幫助我們解決

實際開發過程中的方法,該方法是為了降低對象之間的耦合度,然而解決方法有很多種,所以前人就總結了一些

常用的解決方法為書籍,從而把這本書就稱為設計模式),下面給出單例模式的一個官方定義:確保一個類只有一

個實例,并提供一個全局訪問點。為了幫助大家更好地理解單例模式,大家可以結合下面的類圖來進行理解,以及

后面也會剖析單例模式的實現思路:

Singleton

-uniqueinstance:Singleton

單例模式:確保一個類只有一個實例,、

并提供一個訪問它的全局訪問點-Singleton()

+Getlnstance():Singleton

了解完了一些關于單例模式的基本概念之后,下面就為大家剖析單例模式的實現思路的,因為在我自己學習

單例模式的時候,咋一看單例模式的實現代碼確實很簡單,也很容易看懂,但是我還是覺得它很陌生(這個可能

是看的少的,或者自己在寫代碼中也用的少的緣故),而且心里總會這樣一個疑問一一為什么前人會這樣去實現

單例模式的呢?他們是如何思考的呢?后面經過自己的琢磨也就慢慢理清楚單例模式的實現思路了,并且此時也

不再覺得單例模式陌生了,下面就分享我的一個剖析過程的:

我們從單例模式的概念(確保一個類只有一個實例,并提供一個訪問它的全局訪問點)入手,可以把概念進

行拆分為兩部分:(1)確保一個類只有一個實例;(2)提供一個訪問它的全局訪問點;下面通過采用兩人對

話的方式來幫助大家更快掌握分析思路:

菜鳥:怎樣確保一個類只有一個實例了?

老鳥:那就讓我幫你分析下,你創建類的實例會想到用什么方式來創建的呢?

新手:用new關鍵字啊,只要new下就創建了該類的一個實例了,之后就可以使用該類的一些

屬性和實例方法了

老鳥:那你想過為什么可以使用new關鍵字來創建類的實例嗎?

菜鳥:這個還有條件的嗎?......,哦,我想起來了,如果類定義私有的構造函數就不能在外界

通過new創建實例了(注:有些初學者就會問,有時候我并沒有在類中定義構造函數為什么也可以使

用new來創建對象,那是因為編譯器在背后做了手腳了,當編譯器看到我們類中沒有定義構造函數,

此時編譯器會幫我們生成一個公有的無參構造函數)

老鳥:不錯,回答的很對,這樣你的疑惑就得到解答了啊

菜鳥:那我要在哪里創建類的實例了?

老鳥:你傻啊,當然是在類里面創建了(注:這樣定義私有構造函數就是上面的一個思考過程的,

要創建實例,自然就要有一個變量來保存該實例把,所以就有了私有變量的聲明,但是實現中是定義靜

態私有變量,朋友們有沒有想過——這里為什么定義為靜態的呢?對于這個疑問的解釋為:每個線程都

有自己的線程棧,定義為靜態主要是為了在多線程確保類有一個實例)

菜鳥:哦,現在完全明白了,但是我還有另一個疑問——現在類實例創建在類內部,那外界如何獲

得該的一個實例來使用它了?

老鳥:這個,你可以定義一個公有方法或者屬性來把該類的實例公開出去了(注:這樣就有了公有

方法的定義了,該方法就是提供方法問類的全局訪問點)

通過上面的分析,相信大家也就很容易寫出單例模式的實現代碼了,下面就看看具體的實現代碼(看完之后

你會驚訝道:真是這樣的!):

III單例模式的實現

publicclassSingleton

(

//定義一個靜態變量來保存類的實例

privatestaticSingletonuniqueinstance;

//定義私有構造函數,使外界不能創建該類實例

privateSingleton(){}

III定義公有方法提供一個全局訪問點,同時你也可以定義公有屬性來提供全局訪問點

publicstaticSingletonGetlnstance()

{

//如果類的實例不存在則創建,否則直接返回

if(uniqueinstance==null)

(

uniqueinstance=newSingleton();

)

returnuniqueinstance;

)

)

上面的單例模式的實現在單線程下確實是完美的,然而在多線程的情況下會得到多個Singleton實

例,因為在兩個線程同時運行Getlnstance方法時,此時兩個線程判斷(uniqueinstance==null)這個條

件時都返回真,此時兩個線程就都會創建Singleton的實例,這樣就違背了我們單例模式初衷了,

既然上面的實現會運行多個線程執行,那我們對于多線程的解決方案自然就是使Getlnstance方法

在同一時間只運行一個線程運行就好了,也就是我們線程同步的問題了(對于線程同步大家也可以

參考我線程同步的文章),具體的解決多線程的代碼如下:

III單例模式的實現

publicclassSingleton

//定義一個靜態變量來保存類的實例

privatestaticSingletonuniqueinstance;

//定義一個標識確保線程同步

privatestaticreadonlyobjectlocker=newobject();

//定義私有構造函數,使外界不能創建該類實例

privateSingleton(){}

///定義公有方法提供一個全局訪問點,同時你也可以定義公有屬性來提供全局訪問點

publicstaticSingletonGetlnstancef)

{

//當第一個線程運行到這里時,此時會對locker對象"加鎖",

//當第二個線程運行該方法時,首先檢測到locker對象為"加鎖"狀態,該線程就會

掛起等待第一個線程解鎖

〃lock語句運行完之后(即線程運行完之后)會對該對象"解鎖"

lock(locker)

(

//如果類的實例不存在則創建,否則直接返回

if(uniqueinstance==null)

{

uniqueinstance=newSingleton();

)

)

returnuniqueinstance;

)

)

(2)簡單工廠模式

說到簡單工廠,自然的第一個疑問當然就是什么是簡單工廠模式了?在現實生活中工廠是負責生產產品的,

同樣在設計模式中,簡單工廠模式我們也可以理解為負責生產對象的一個類,我們平常編程中,當使用“new”關鍵

字創建一個對象時,此時該類就依賴與這個對象,也就是他們之間的耦合度高,當需求變化時,我們就不得不去

修改此類的源碼,此時我們可以運用面向對象(00)的很重要的原則去解決這一的問題,該原則就是——封裝

改變,既然要封裝改變,自然也就要找到改變的代碼,然后把改變的代碼用類來封裝,這樣的一種思路也就是

我們簡單工廠模式的實現方式了。下面通過一個現實生活中的例子來引出簡單工廠模式。在外面打工的人,免不

了要經常在外面吃飯,當然我們也可以自己在家做飯吃,但是自己做飯吃麻煩,因為又要自己買菜,然而,出去

吃飯就完全沒有這些麻煩的,我們只需要到餐館點菜就可以了,買菜的事情就交給餐館做就可以了,這里餐館就

充當簡單工廠的角色,下面讓我們看看現實生活中的例子用代碼是怎樣來表現的。自己做飯的情況:

///自己做飯的情況

///沒有簡單工廠之前,客戶想吃什么菜只能自己炒的

publicclassCustomer

(

///燒菜方法

publicstaticFoodCook(stringtype)

(

Foodfood=null;

//客戶A說:我想吃西紅柿炒蛋怎么辦?

//客戶B說:那你就自己燒啊

//客戶A說:好吧,那就自己做吧

if(type.Equals("西紅柿炒蛋”))

food=newTomatoScrambledEggs();

//我又想吃土豆肉絲,這個還是得自己做

//我覺得自己做好累哦,如果能有人幫我做就好了?

elseif(type.Equals(〃土豆肉絲”))

food=newShreddedPorkWithPotatoes();

)

returnfood;

}

staticvoidMain(string[]args)

{

//做西紅柿炒蛋

Foodfoodl=Cook(〃西紅柿炒蛋”);

foodl.Print();

Foodfood2=Cook(〃土豆肉絲〃);

food2.Print();

Console.Read();

///菜抽象類

publicabstractclassFood

//輸出點了什么菜

publicabstractvoidPrint();

///西紅柿炒雞蛋這道菜

publicclassTomatoScrambledEggs:Food

{

publicoverridevoidPrint()

Console.WriteLine(〃一份西紅柿炒蛋!〃);

)

///土豆肉絲這道菜

publicclassShreddedPorkWithPotatoes:Food

publicoverridevoidPrint()

Console.WriteLine(z,一份土豆肉絲〃);

自己做飯,如果我們想吃別的菜時,此時就需要去買這種菜和洗菜這些繁瑣的操作,有了餐館(也就是簡單

工廠)之后,我們就可以把這些操作交給餐館去做,此時消費者(也就是我們)對菜(也就是具體對象)的依賴

關系從直接變成的間接的,這樣就是實現了面向對象的另一個原則——降低對象之間的耦合度,下面就具體看看

有了餐館之后的實現代碼(即簡單工廠的實現):

///顧客充當客戶端,負責調用簡單工廠來生產對象

///即客戶點菜,廚師(相當于簡單工廠)負責燒菜(生產的對象)

classCustomer

{

staticvoidMain(string[]args)

(

//客戶想點一個西紅柿炒蛋

Foodfoodl=FoodSimpleFactory.CreateFood("西紅柿炒蛋”);

foodl.Print();

//客戶想點一個土豆肉絲

Foodfood2=FoodSimpleFactory.CreateFood(“土豆肉絲”);

food2.Print();

Console.Read();

}

}

///菜抽象類

publicabstractclassFood

(

//輸出點了什么菜

publieabstractvoidPrint();

)

///西紅柿炒雞蛋這道菜

publicclassTomatoScrambledEggs:Food

{

publicoverridevoidPrint()

(

Console.WriteLine("一份西紅柿炒蛋!”);

)

)

///土豆肉絲這道菜

publicclassShreddedPorkWithPotatoes:Food

{

publicoverridevoidPrint()

(

Console.WriteLine("一份土豆肉絲“);

)

)

///簡單工廠類,負責炒菜

publicclassFoodSimpleFactory

{

publicstaticFoodCreateFood(stringtype)

(

Foodfood=null;

if(type.Equals("土豆肉絲”))

(

food=newShreddedPorkWithPotatoes();

)

elseif(type.Equals("西紅柿炒蛋”))

food=newTomatoScrambledEggs();

returnfood;

優點與缺點

看完簡單工廠模式的實現之后,你和你的小伙伴們肯定會有這樣的疑惑(因為我學習的時候也有)——這樣

我們只是把變化移到了工廠類中罷了,好像沒有變化的問題,因為如果客戶想吃其他菜時,此時我們還是需要修

改工廠類中的方法(也就是多加case語句,沒應用簡單工廠模式之前,修改的是客戶類)。我首先要說:你和

你的小伙伴很對,這個就是簡單工廠模式的缺點所在(這個缺點后面介紹的工廠方法可以很好地解決),然而,

簡單工廠模式與之前的實現也有它的優點:

?簡單工廠模式解決了客戶端直接依賴于具體對象的問題,客戶端可以消除直接創建對象的責任,而僅僅是

消費產品。簡單工廠模式實現了對責任的分割。

?簡單工廠模式也起到了代碼復用的作用,因為之前的實現(自己做飯的情況)中,換了一個人同樣要去在

自己的類中實現做菜的方法,然后有了簡單工廠之后,去餐館吃飯的所有人都不用那么麻煩了,只需要負

責消費就可以了。此時簡單工廠的燒菜方法就讓所有客戶共用了。(同時這點也是簡單工廠方法的缺點——

因為工廠類集中了所有產品創建邏輯,一旦不能正常工作,整個系統都會受到影響,也沒什么不好理解的,

就如事物都有兩面性一樣道理)

雖然上面已經介紹了簡單工廠模式的缺點,下面還是總結下簡單工廠模式的缺點:

?工廠類集中了所有產品創建邏輯,一旦不能正常工作,整個系統都會受到影響(通俗地意思就是:一旦餐

館沒飯或者關門了,很多不愿意做飯的人就沒飯吃了)

?系統擴展困難,一旦添加新產品就不得不修改工廠邏輯,這樣就會造成工廠邏輯過于復雜。

了解了簡單工廠模式之后的優缺點之后,我們之后就可以知道簡單工廠的應用場景了:

?當工廠類負責創建的對象比較少時可以考慮使用簡單工廠模式。

?客戶如果只知道傳入工廠類的參數,對于如何創建對象的邏輯不關心時可以考慮使用簡單工廠模式

(3)工廠方法模式

工廠方法模式之所以可以解決簡單工廠的模式,是因為它的實現把具體產品的創建推遲到子類中,此時工廠

類不再負責所有產品的創建,而只是給出具體工廠必須實現的接口,這樣工廠方法模式就可以允許系統不修改工

廠類邏輯的情況下來添加新產品,這樣也就克服了簡單工廠模式中缺點。下面看下工廠模式的具體實現代碼(這

里還是以簡單工廠模式中點菜的例子來實現):

namespace設計模式之工廠方法模式

(

///菜抽象類

publicabstractclassFood

(

//輸出點了什么菜

publieabstractvoidPrint();

}

///西紅柿炒雞蛋這道菜

publicclassTomatoScrambledEggs:Food

{

publicoverridevoidPrint()

(

Console.WriteLine("西紅柿炒蛋好了!");

)

}

///土豆肉絲這道菜

publicclassShreddedPorkWithPotatoes:Food

{

publicoverridevoidPrint()

(

Console.WriteLine("土豆肉絲好了“);

)

)

///抽象工廠類

publicabstractclassCreator

{

//工廠方法

publicabstractFoodCreateFoddFactory();

)

///西紅柿炒蛋工廠類

publicclassTomatoScrambledEggsFactory:Creator

(

///負責創建西紅柿炒蛋這道菜

publicoverrideFoodCreateFoddFactory()

(

returnnewTomatoScrambledEggs();

)

///土豆肉絲工廠類

publicclassShreddedPorkWithPotatoesFactory:Creator

(

///負責創建土豆肉絲這道菜

publieoverrideFoodCreateFoddFactory()

(

returnnewShreddedPorkWithPotatoes();

)

)

///客戶端調用

classClient

(

staticvoidMain(string[]args)

(

//初始化做菜的兩個工廠O

CreatorshreddedPorkWithPotatoesFactory=new

ShreddedPorkWithPotatoesFactory();

CreatortomatoScrambledEggsFactory=newTomatoScrambledEggsFactory();

//開始做西紅柿炒蛋

FoodtomatoScrambleEggs=tomatoScrambledEggsFactory.CreateFoddFactory();

tomatoScrambleEggs.Print();

〃開始做土豆肉絲

FoodShreddedPorkWithPotatoes=

shreddedPorkWithPotatoesFactory.CreateFoddFactory();

ShreddedPorkWithPotatoes.Print();

Console.ReadO;

)

)

)

使用工廠方法實現的系統,如果系統需要添加新產品時,我們可以利用多態性來完成系統的擴展,對于抽象

工廠類和具體工廠中的代碼都不需要做任何改動。例如,我們我們還想點一個“肉末茄子”,此時我們只需要定義

一個肉末茄子具體工廠類和肉末茄子類就可以。而不用像簡單工廠模式中那樣去修改工廠類中的實現(具體指添

加case語句)。具體代碼為:

///肉末茄子這道菜

publicclassMincedMeatEggplant:Food

(

///重寫抽象類中的方法

publicoverridevoidPrint()

(

Console.WriteLine("肉末茄子好了“);

)

)

///肉末茄子工廠類,負責創建肉末茄子這道菜

publicclassMincedMeatEggplantFactory:Creator

(

///負責創建肉末茄子這道菜

publicoverrideFoodCreateFoddFactory()

returnnewMincedMeatEggplant();

Ill客戶端調用

classClient

staticvoidMain(string[]args)

(

//如果客戶又想點肉末茄子了

//再另外初始化一個肉末茄子工廠

CreatorminceMeatEggplantFactor=newMincedMeatEggplantFactory();

//利用肉末茄子工廠來創建肉末茄子這道菜

FoodminceMeatEggplant=minceMeatEggplantFactor.CreateFoddFactory();

minceMeatEggplant.Print();

Console.Read();

)

)

在工廠方法模式中,工廠類與具體產品類具有平行的等級結構,它們之間是一一對應的。針對UML圖的解釋如

下:

Creator類:充當抽象工廠角色,任何具體工廠都必須繼承該抽象類

TomatoScrambledEggsFactory和ShreddedPorkWithPotatoesFactory類:充當具體工廠角色,用來

創建具體產品

Food類:充當抽象產品角色,具體產品的抽象類。任何具體產品都應該繼承該類

TomatoScrambledEggs和ShreddedPorkWithPotatoes類:充當具體產品角色,實現抽象產品類對定

義的抽象方法,由具體工廠類創建,它們之間有一一對應的關系。

(4)抽象工廠模式

下面就以生活中“絕味”連鎖店的例子來實現一個抽象工廠模式。例如,絕味鴨脖想在江西南昌和上海開分

店,但是由于當地人的口味不一樣,在南昌的所有絕味的東西會做的辣一點,而上海不喜歡吃辣的,所以上海的

所有絕味的東西都不會做的像南昌的那樣辣,然而這點不同導致南昌絕味工廠和上海的絕味工廠生成所有絕味的

產品都不同,也就是某個具體工廠需要負責一系列產品(指的是絕味所有食物)的創建工作,下面就具體看看如何

使用抽象工廠模式來實現這種情況。

///下面以絕味鴨脖連鎖店為例子演示下抽象工廠模式

///因為每個地方的喜歡的口味不一樣,有些地方喜歡辣點的,有些地方喜歡吃不辣點

///客戶端調用

6classClient

7{

8staticvoidMain(string[]args)

9(

10//南昌工廠制作南昌的鴨脖和鴨架

11AbstractFactorynanChangFactory=newNanChangFactory();

12YaBonanChangYabo=nanChangFactory.CreateYaBoO;

13nanChangYabo.Print();

14YajiananChangYajia=nanChangFactory.CreateYaJia();

15nanChangYajia.Print();

17//上海工廠制作上海的鴨脖和鴨架

18AbstractFactoryshangHaiFactory=newShangHaiFactory();

19shangHaiFactory.CreateYaBo().Print();

20shangHaiFactory.CreateYaJia().Print();

22Console.Read();

23)

24)

27///抽象工廠類,提供創建兩個不同地方的鴨架和鴨脖的接口

29publicabstractclassAbstractFactory

30(

31//抽象工廠提供創建一系列產品的接口,這里作為例子,只給出了絕味中鴨脖和鴨

架的創建接口

32publieabstractYaBoCreateYaBoO;

33publicabstractYajiaCreateYaJia();

34)

37〃/南昌絕味工廠負責制作南昌的鴨脖和鴨架

39publieclassNanChangFactory:AbstractFactory

40(

41//制作南昌鴨脖

42publicoverrideYaBoCreateYaBo()

43{

44returnnewNanChangYaBo();

45)

46//制作南昌鴨架

47publicoverrideYajiaCreateYaJia()

48(

49returnnewNanChangYaJia();

50)

51

54///上海絕味工廠負責制作上海的鴨脖和鴨架

56publicclassShangHaiFactory:AbstractFactory

57(

58//制作上海鴨脖

59publicoverrideYaBoCreateYaBo()

60{

61returnnewShangHaiYaBo();

62)

63//制作上海鴨架

64publicoverrideYajiaCreateYaJia()

65{

66returnnewShangHaiYaJia();

67)

68)

71///鴨脖抽象類,供每個地方的鴨脖類繼承

73publicabstractclassYaBo

74(

76///打印方法,用于輸出信息

78publicabstractvoidPrint();

79)

82///鴨架抽象類,供每個地方的鴨架類繼承

84publicabstractclassYajia

85(

87///打印方法,用于輸出信息

89publicabstractvoidPrint();

90)

93///南昌的鴨脖類,因為江西人喜歡吃辣的,所以南昌的鴨脖稍微會比上海做的辣

95publicclassNanChangYaBo:YaBo

96(

97publicoverridevoidPrint()

98(

99Console.WriteLine("南昌的鴨脖”);

100)

101)

104///上海的鴨脖沒有南昌的鴨脖做的辣

106publicclassShangHaiYaBo:YaBo

107(

108publicoverridevoidPrint()

109(

110Console.WriteLine("上海的鴨脖”);

111)

112)

115///南昌的鴨架

117publicclassNanChangYaJia:Yajia

118(

119publicoverridevoidPrint()

120

121Console.WriteLine(“南昌的鴨架子”);

122)

123)

126///上海的鴨架

128publicclassShangHaiYaJia:Yajia

129(

130publicoverridevoidPrint()

131(

132Console.WriteLine(“上海的鴨架子”);

133)

134)

上面代碼中都有詳細的注釋,這里就不再解釋上面的代碼了,下面就具體看看抽象工廠模式的定義吧(理解定義

可以參考上面的實現來加深理解):

抽象工廠模式;提供一個創建產品的接口來負責創建相關或依賴的對象,而不具體明確指定具體類

抽象工廠允許客戶使用抽象的接口來創建一組相關產品,而不需要知道或關心實際生產出的具體產品是什么。這

樣客戶就可以從具體產品中被解耦。下面通過抽象工模式的類圖來了解各個類中之間的關系:

2.3抽象工廠應對需求變更

看完上面抽象工廠的實現之后,如果“絕味”公司又想在湖南開一家分店怎么辦呢?因為湖南人喜歡吃麻辣

的,下面就具體看看應用了抽象工廠模式的系統是如何應對這種需求的。

///如果絕味又想開一家湖南的分店時,因為湖南喜歡吃麻的

///所以這是有需要有一家湖南的工廠專門制作

publicclassHuNanFactory:AbstractFactory

(

//制作湖南鴨脖

publicoverrideYaBoCreateYaBo()

(

returnnewHuNanYaBo();

)

//制作湖南鴨架

publicoverrideYajiaCreateYaJia()

(

returnnewHuNanYajia();

}

}

///湖南的鴨脖

publicclassHuNanYaBo:YaBo

(

publicoverridevoidPrint()

(

Console.WriteLine(“湖南的鴨脖”);

}

///湖南的鴨架

publicclassHuNanYajia:Yajia

(

publicoverridevoidPrint()

(

Console.WriteLine("湖南的鴨架子”);

)

)

此時,只需要添加三個類:一個是湖南具體工廠類,負責創建湖南口味的鴨脖和鴨架,另外兩個類是具有湖南口

味的鴨脖類和鴨架類。從上面代碼看出,抽象工廠對于系列產品的變化支持“開放——封閉”原則(指的是要求

系統對擴展開放,對修改封閉),擴展起來非常簡便,但是,抽象工廠對于添加新產品這種情況就不支持“開放

——封閉“原則,這也是抽象工廠的缺點所在,這點會在第四部分詳細介紹。

抽象工廠的分析

抽象工廠模式將具體產品的創建延遲到具體工廠的子類中,這樣將對象的創建封裝起來,可以減少客戶端與

具體產品類之間的依賴,從而使系統耦合度低,這樣更有利于后期的維護和擴展,這真是抽象工廠模式的優點所

在,然后抽象模式同時也存在不足的地方。下面就具體看下抽象工廠的缺點(缺點其實在前面的介紹中以已經涉

及了):

抽象工廠模式很難支持新種類產品的變化。這是因為抽象工廠接口中已經確定了可以被創建的產品集合,如果

需要添加新產品,此時就必須去修改抽象工廠的接口,這樣就涉及到抽象工廠類的以及所有子類的改變,這樣

也就違背了“開發——封閉”原則。

知道了抽象工廠的優缺點之后,也就能很好地把握什么情況下考慮使用抽象工廠模式了,下面就具體看看使

用抽象工廠模式的系統應該符合那幾個前提:

一個系統不要求依賴產品類實例如何被創建、組合和表達的表達,這點也是所有工廠模式應用的前提。

這個系統有多個系列產品,而系統中只消費其中某一系列產品

?系統要求提供一個產品類的庫,所有產品以同樣的接口出現,客戶端不需要依賴具體實現。

(5)建造者模式(BUILDER)

在軟件系統中,有時需要創建一個復雜對象,并且這個復雜對象由其各部分子對象通過一定的步驟組合而成。例如一個采購系統中,

如果需要采購員去采購一批電腦時,在這個實際需求中,電腦就是一個復雜的對象,它是由CPU、主板、硬盤、顯卡、機箱等組

裝而成的,如果此時讓采購員一臺一臺電腦去組裝的話真是要累死采購員了,這里就可以采用建造者模式來解決這個問題,我們可

以把電腦的各個組件的組裝過程封裝到一個建造者類對象里,建造者只要負責返還給客戶端全部組件都建造完畢的產品對象就可以

了。然而現實生活中也是如此的,如果公司要采購一批電腦,此時采購員不可能自己去買各個組件并把它們組織起來,此時采購員

只需要像電腦城的老板說自己要采購什么樣的電腦就可以了,電腦城老板自然會把組裝好的電腦送到公司。下面就以這個例子來展

開建造者模式的介紹。

二、建造者模式的詳細介紹

在這個例子中,電腦城的老板是直接與客戶(也就是指采購員)聯系的,然而電腦的組裝是由老板指揮裝機

人員去把電腦的各個部件組裝起來,真真負責創建產品(這里產品指的就是電腦)的人就是電腦城的裝機人員。

理清了這個邏輯過程之后,下面就具體看下如何用代碼來表示這種現實生活中的邏輯過程:

1usingSystem;

2usingSystem.Collections.Generic;

3usingSystem.Linq;

4usingSystem.Text;

8///以組裝電腦為例子

9///每臺電腦的組成過程都是一致的,但是使用同樣的構建過程可以創建不同的表示(即可以

組裝成不一樣的電腦,配置不一樣)

10///組裝電腦的這個場景就可以應用建造者模式來設計

12namespace設計模式之建造者模式

13(

15///客戶類

17classCustomer

18{

19staticvoidMain(string[]args)

20{

21//客戶找到電腦城老板說要買電腦,這里要裝兩臺電腦

22//創建指揮者和構造者

23Directordirector=newDirector();

24Builderbl=newConcreteBuilderl();

25Builderb2=newConcreteBuilder2();

27//老板叫員工去組裝第一臺電腦

28director.Construct(bl);

30//組裝完,組裝人員搬來組裝好的電腦

31Computercomputeri=bl.GetComputer();

32computeri.Show();

33

34//老板叫員工去組裝第二臺電腦

35director.Construct(b2);

36Computercomputer2=b2.GetComputer();

37computer2.Show();

39Console.Read();

40)

41)

42

44///小王和小李難道會自愿地去組裝嘛,誰不想休息的,這必須有一個人叫他們去組裝才

會去的

45///這個人當然就是老板了,也就是建造者模式中的指揮者

46///指揮創建過程類

48publieclassDirector

49(

50//組裝電腦

51publicvoidConstruct(Builderbuilder)

52(

53builder.BuildPartCPU();

54builder.BuildPartMainBoard();

55)

56)

59///電腦類

61publieclassComputer

62(

63//電腦組件集合

64privateIList<string>parts=newList<string>();

65

66//把單個組件添加到電腦組件集合中

67publicvoidAdd(stringpart)

68(

69parts.Add(part);

70)

72publievoidShow()

73(

74Console.WriteLine("電腦開始在組裝......”);

75foreach(stringpartinparts)

76{

77Console.WriteLine('組件"+part+"已裝好”);

78)

79

80Console.WriteLine("電腦組裝好了“);

81)

82)

85///抽象建造者,這個場景下為〃組裝人〃,這里也可以定義為接口

87publieabstractclassBuilder

88(

89//裝CPU

90publicabstractvoidBuildPartCPU();

91//裝主板

92publicabstractvoidBuildPartMainBoard();

94//當然還有裝硬盤,電源等組件,這里省略

96//獲得組裝好的電腦

97publicabstractComputerGetComputer();

98)

101///具體創建者,具體的某個人為具體創建者,例如:裝機小王啊

103publicclassConcreteBuilderl:Builder

104(

105Computercomputer=newComputer();

106publicoverridevoidBuildPartCPU()

107(

108computer.Add("CPUl");

109)

111publicoverridevoidBuildPartMainBoardO

112(

113computer.Add(/zMainboardl");

114)

116publicoverrideComputerGetComputer()

117(

118returncomputer;

119)

120)

123///具體創建者,具體的某個人為具體創建者,例如:裝機小李啊

124///又裝另一臺電腦了

126publicclassConcreteBuilder2:Builder

127(

128Computercomputer=newComputer();

129publicoverridevoidBuildPartCPU()

130(

131computer.Add(“CPU2");

132)

134publicoverridevoidBuildPartMainBoard()

135{

136computer.Add(/zMainboard2");

137)

139publicoverrideComputerGetComputer()

140(

141returncomputer;

142)

143}

144

介紹完了建造者模式的具體實現之后,下面具體看下建造者模式的具體定義是怎樣的。

建造者模式(BuilderPattern):將一個復雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的

表示。

建造者模式使得建造代碼與表示代碼的分離,可以使客戶端不必知道產品內部組成的細節,從而降低了客戶端與

具體產品之間的耦合度,下面通過類圖來幫助大家更好地理清建造者模式中類之間的關系。

建造者模式的分析

介紹完了建造者模式的具體實現之后,讓我們總結下建造模式的實現要點:

1.在建造者模式中,指揮者是直接與客戶端打交道的,指揮者將客戶端創建產品的請求劃分為對各個部件的建造請

求,再將這些請求委派到具體建造者角色,具體建造者角色是完成具體產品的構建工作的,卻不為客戶所知道。

2.建造者模式主要用于“分步驟來構建一個復雜的對象”,其中“分步驟”是一個固定的組合過程,而復雜對象的各個

部分是經常變化的(也就是說電腦的內部組件是經常變化的,這里指的的變化如硬盤的大小變了,CPU由單核

變雙核等)。

3.產品不需要抽象類,由于建造模式的創建出來的最終產品可能差異很大,所以不大可能提煉出一個抽象產品類。

4.在前面文章中介紹的抽象工廠模式解決了“系列產品”的需求變化,而建造者模式解決的是“產品部分”的需要變

化。

5.由于建造者隱藏了具體產品的組裝過程,所以要改變一個產品的內部表示,只需要再實現一個具體的建造者就可

以了,從而能很好地應對產品組成組件的需求變化。

(6)原型模式(PROTOTYPE)

在軟件系統中,當創建一個類的實例的過程很昂貴或很復雜,并且我們需要創建多個這樣類的實例時,如果

我們用new操作符去創建這樣的類實例,這未免會增加創建類的復雜度和耗費更多的內存空間,因為這樣在內

存中分配了多個一樣的類實例對象,然后如果采用工廠模式來創建這樣的系統的話,隨著產品類的不斷增加,導

致子類的數量不斷增多,反而增加了系統復雜程度,所以在這里使用工廠模式來封裝類創建過程并不合適,然而

原型模式可以很好地解決這個問題,因為每個類實例都是相同的,當我們需要多個相同的類實例時,沒必要每次

都使用new運算符去創建相同的類實例對象,此時我們一般思路就是想——只創建一個類實例對象,如果后面

需要更多這樣的實例,可以通過對原來對象拷貝一份來完成創建,這樣在內存中不需要創建多個相同的類實例,

從而減少內存的消耗和達到類實例的復用。然而這個思路正是原型模式的實現方式。下面就具體介紹下設計模

式中的原型設計模式。

二、原型模式的詳細介紹

在現實生活中,也有很多原型設計模式的例子,例如,細胞分裂的過程,一個細胞的有絲分裂產生兩個相同的細

胞;還有西游記中孫悟空變出后孫的本領和火影忍者中鳴人的隱分身忍術等。下面就以孫悟空為例子來演示下原

型模式的實現。具體的實現代碼如下:

〃/火影忍者中鳴人的影分身和孫悟空的的變都是原型模式

classClient

(

staticvoidMain(string[]args)

{

//孫悟空原型

MonkeyKingPrototypeprototypeMonkeyKing=new

ConcretePrototype(〃MonkeyKing〃);

//變一個

MonkeyKingPrototypecloneMonkeyKing=prototypeMonkeyKing.Clone()as

ConcretePrototype;

Console.WriteLine(/zClonedl:\t/z+cloneMonkeyKing.Id);

//變兩個

MonkeyKingPrototypecloneMonkeyKing2=prototypeMonkeyKing.Clone()as

ConcretePrototype;

Console.WriteLine(/zCloned2:\t/z+cloneMonkeyKing2.Id);

Console.ReadLine();

)

)

///孫悟空原型

publicabstractclassMonkeyKingPrototype

{

publicstringId{get;set;}

publicMonkeyKingPrototype(stringid)

{

this.Id=id;

)

//克隆方法,即孫大圣說“變”

publicabstractMonkeyKingPrototypeClone();

}

///創建具體原型

publicclassConcretePrototype:MonkeyKingPrototype

{

publicConcretePrototype(stringid)

:base(id)

{}

///淺拷貝

publicoverrideMonkeyKingPrototypeClone()

//調用MemberwiseClone方法實現的是淺拷貝,另外還有深拷貝

return(MonkeyKingPrototype)this.MemberwiseClone();

)

)

上面原型模式的運行結果為(從運行結果可以看出,創建的兩個拷貝對象的ID屬性都是與原型對象ID屬性一

樣的):

上面代碼實現的淺拷貝的方式,淺拷貝是指當對象的字段值被拷貝時,字段引用的對象不會被拷貝。例如,如果

一個對象有一個指向字符串的字段,并且我們對該對象做了一個淺拷貝,那么這兩個對象將引用同一個字符串,

而深拷貝是對對象實例中字段引用的對象也進行拷貝,如果一個對象有一個指向字符串的字段,并且我們對該對

象進行了深拷貝的話,那么我們將創建一個對象和一個新的字符串,新的對象將引用新的字符串。也就是說,執

行深拷貝創建的新對象和原來對象不會共享任何東西,改變一個對象對另外一個對象沒有任何影響,而執行淺拷

貝創建的新對象與原來對象共享成員,改變一個對象,另外一個對象的成員也會改變。

介紹完原型模式的實現代碼之后,下面看下原型模式的類圖,通過類圖來理清原型模式實現中類之間的關系。具

體類圖如下:

Name:原空模式

AuttxxiLearnmgHard

三、原型模式的優缺點

原型模式的優點有:

1,原型模式向客戶隱藏了創建新實例的復雜性

2.原型模式允許動態增加或較少產品類。

3.原型模式簡化了實例的創建結構,工廠方法模式需要有一個與產品類等級結構相同的等級結構,而原型模式不需

要這樣。

4.產品類不需要事先確定產品的等級結構,因為原型模式適用于任何的等級結構

原型模式的缺點有:

1-每個類必須配備一個克隆方法

2.配備克隆方法需要對類的功能進行通盤考慮,這對于全新的類不是很難,但對于已有的類不一定很容易,特別

當一個類引用不支持串行化的間接對象,或者引用含有循環結構的時候。

(7)適配器模式(ADAPTER)

下面讓我們看看適配器的定義,適配器模式——把一個類的接口變換成客戶端所期待的另一種接口,從而使

原本接口不匹配而無法一起工作的兩個類能夠在一起工作。適配器模式有類的適配器模式和對象的適配器模式兩

種形式,下面我們分別討論這兩種形式的實現和給出對應的類圖來幫助大家理清類之間的關系。

類的適配器模式實現

在這里以生活中的一個例子來進行演示適配器模式的實現,具體場景是:在生活中,我們買的電器插頭是2

個孔的,但是我們買的插座只有三個孔的,此時我們就希望電器的插頭可以轉換為三個孔的就好,這樣我們就可

以直接把它插在插座上,此時三個孔插頭就是客戶端期待的另一種接口,自然兩個孔的插頭就是現有的接口,適

配器模式就是用來完成這種轉換的,具體實現代碼如下:

usingSystem;

///這里以插座和插頭的例子來詮釋適配器模式

///現在我們買的電器插頭是2個孔,但是我們買的插座只有3個孔的

///這是我們想把電器插在插座上的話就需要一個電適配器

namespace設計模式之適配器模式

(

///客戶端,客戶想要把2個孔的插頭轉變成三個孔的插頭,這個轉變交給適配器就好

///既然適配器需要完成這個功能,所以它必須同時具體2個孔插頭和三個孔插頭的特征

classClient

{

staticvoidMain(string[]args)

(

//現在客戶端可以通過電適配要使用2個孔的插頭了

IThreeHolethreehole=newPowerAdapter();

threehole.Request();

Console.ReadLine();

)

)

///三個孔的插頭,也就是適配器模式中的目標角色

publicinterfaceIThreeHole

{

voidRequest();

)

///兩個孔的插頭,源角色一一需要適配的類

publicabstractclassTwoHole

{

publicvoidSpecificRequest()

Console.WriteLine(“我是兩個孔的插頭“);

///適配器類,接口要放在類的后面

///適配器類提供了三個孔插頭的行為,但其本質是調用兩個孔插頭的方法

publicclassPowerAdapter:TwoHole,IThreeHole

///實現三個孔插頭接口方法

publicvoidRequest()

//調用兩個孔插頭方法

this.SpecificRequest();

從上面代碼中可以看出,客戶端希望調用Request方法(即三個孔插頭),但是我們現有的類(即2個孔

的插頭)并沒有Request方法,它只有SpecificRequest方法(即兩個孔插頭本身的方法),然而適配器類(適

配器必須實現三個孔插頭接口和繼承兩個孔插頭類)可以提供這種轉換,它提供了Request方法的實現(其內

部調用的是兩個孔插頭,因為適配器只是一個外殼罷了,包裝著兩個孔插頭(因為只有這樣,電器才能使用),

并向外界提供三個孔插頭的外觀,)以供客戶端使用。

類圖

上面實現中,因為適配器(PowerAdapter類)與源角色(TwoHole類)是繼承關系,所以該適配器模式是類

的適配器模式,具體對應的類圖為:

<〈Interface>>

ClientTarget?源角色:被適配器適配的類^1

+Reque<;tO

△△

目標角色:客戶湍期望的接口,

由于C:不支持多維承,

所以IBTarge虎義為接口

對象的適配器模式

上面都是類的適配器模式的介紹,然而適配器模式還有另外一種形式——

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論