一、引言
?? 從今天開始我們開始講【結(jié)構(gòu)型】設(shè)計模式,【結(jié)構(gòu)型】設(shè)計模式有如下幾種:適配器模式、橋接模式、裝飾模式、組合模式、外觀模式、享元模式、代理模式?!緞?chuàng)建型】的設(shè)計模式解決的是對象創(chuàng)建的問題,那【結(jié)構(gòu)型】設(shè)計模式解決的是類和對象的組合關(guān)系的問題。今天我們就開始講【結(jié)構(gòu)型】設(shè)計模式里面的第一個設(shè)計模式,中文名稱:適配器模式,英文名稱:Adapter Pattern。說起這個模式其實很簡單,在現(xiàn)實生活中也有很多實例,比如:我們手機的充電器,充電器的接頭,有的是把兩相電轉(zhuǎn)換為三相電的,當然也有把三相電轉(zhuǎn)換成兩相電的。我們經(jīng)常使用筆記本電腦,筆記本電腦的工作電壓和我們家里照明電壓是不一致的,當然也就需要充電器把照明電壓轉(zhuǎn)換成筆記本的工作電壓,只有這樣筆記本電腦才可以正常工作。太多了,就不一一列舉了。我們只要記住一點,適配就是轉(zhuǎn)換,把不能在一起工作的兩樣?xùn)|西通過轉(zhuǎn)換,讓他們可以在一起工作。
二、適配器模式的詳細介紹
2.1、動機(Motivate)
?? 在軟件系統(tǒng)中,由于應(yīng)用環(huán)境的變化,常常需要將“一些現(xiàn)存的對象”放在新的環(huán)境中應(yīng)用,但是新環(huán)境要求的接口是這些現(xiàn)存對象所不滿足的。如何應(yīng)對這種“遷移的變化”?如何既能利用現(xiàn)有對象的良好實現(xiàn),同時又能滿足新的應(yīng)用環(huán)境所要求的接口?
2.2、意圖(Intent)
?? 將一個類的接口轉(zhuǎn)換成客戶希望的另一個接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些類可以一起工作。?????????????????????????????????????????????????????????????????? --《設(shè)計模式》Gof
2.3、結(jié)構(gòu)圖(Structure)
?? 適配器有兩種結(jié)構(gòu)
1】、-對象適配器(更常用)
? ?? ???
?? 對象適配器使用的是對象組合的方案,它的Adapter核Adaptee的關(guān)系是組合關(guān)系。
?? OO中優(yōu)先使用組合模式,組合模式不適用再考慮繼承。因為組合模式更加松耦合,而繼承是緊耦合的,父類的任何改動都要導(dǎo)致子類的改動。
2】、-類適配器 ????? ? ????????
2.4、模式的組成
????? 可以看出,在適配器模式的結(jié)構(gòu)圖有以下角色:
????? (1)、目標角色(Target):定義Client使用的與特定領(lǐng)域相關(guān)的接口。 ? ? ??? ? (2)、客戶角色(Client):與符合Target接口的對象協(xié)同。 ? ????? (3)、被適配角色(Adaptee):定義一個已經(jīng)存在并已經(jīng)使用的接口,這個接口需要適配。 ? ????? (4)、適配器角色(Adapte) :適配器模式的核心。它將對被適配Adaptee角色已有的接口轉(zhuǎn)換為目標角色Target匹配的接口。對Adaptee的接口與Target接口進行適配.
2.5 適配器模式的具體實現(xiàn)
?? 由于適配器模式有兩種實現(xiàn)結(jié)構(gòu),今天我們針對每種都實現(xiàn)了自己的方式。
?? 1、對象的是適配器模式實現(xiàn)
1 namespace 對象的適配器模式
2 {
3 ///<summary>
4 ///家里只有兩個孔的插座,也懶得買插線板了,還要花錢,但是我的手機是一個有3個小柱子的插頭,明顯直接搞不定,那就適配吧
5 ///</summary>
6 class Client
7 {
8 static void Main(string[] args)
9 {
10 //好了,現(xiàn)在就可以給手機充電了
11 TwoHoleTarget homeTwoHole = new ThreeToTwoAdapter();
12 homeTwoHole.Request();
13 Console.ReadLine();
14 }
15 }
16
17 /// <summary>
18 /// 我家只有2個孔的插座,也就是適配器模式中的目標(Target)角色,這里可以寫成抽象類或者接口
19 /// </summary>
20 public class TwoHoleTarget
21 {
22 // 客戶端需要的方法
23 public virtual void Request()
24 {
25 Console.WriteLine("兩孔的充電器可以使用");
26 }
27 }
28
29 /// <summary>
30 /// 手機充電器是有3個柱子的插頭,源角色——需要適配的類(Adaptee)
31 /// </summary>
32 public class ThreeHoleAdaptee
33 {
34 public void SpecificRequest()
35 {
36 Console.WriteLine("我是3個孔的插頭也可以使用了");
37 }
38 }
39
40 /// <summary>
41 /// 適配器類,TwoHole這個對象寫成接口或者抽象類更好,面向接口編程嘛
42 /// </summary>
43 public class ThreeToTwoAdapter : TwoHoleTarget
44 {
45 // 引用兩個孔插頭的實例,從而將客戶端與TwoHole聯(lián)系起來
46 private ThreeHoleAdaptee threeHoleAdaptee = new ThreeHoleAdaptee();
47 //這里可以繼續(xù)增加適配的對象。。
48
49 /// <summary>
50 /// 實現(xiàn)2個孔插頭接口方法
51 /// </summary>
52 public override void Request()
53 {
54 //可以做具體的轉(zhuǎn)換工作
55 threeHoleAdaptee.SpecificRequest();
56 //可以做具體的轉(zhuǎn)換工作
57 }
58 }
59 }
?? 2、類的適配器模式實現(xiàn)
1 namespace 設(shè)計模式之適配器模式
2 {
3 /// <summary>
4 /// 這里手機充電器為例,我們的家的插座是兩相電的,但是手機的插座接頭是三相電的
5 /// </summary>
6 class Client
7 {
8 static void Main(string[] args)
9 {
10 //好了,現(xiàn)在可以充電了
11 ITwoHoleTarget change = new ThreeToTwoAdapter();
12 change.Request();
13 Console.ReadLine();
14 }
15 }
16
17 /// <summary>
18 /// 我家只有2個孔的插座,也就是適配器模式中的目標角色(Target),這里只能是接口,也是類適配器的限制
19 /// </summary>
20 public interface ITwoHoleTarget
21 {
22 void Request();
23 }
24
25 /// <summary>
26 /// 3個孔的插頭,源角色——需要適配的類(Adaptee)
27 /// </summary>
28 public abstract class ThreeHoleAdaptee
29 {
30 public void SpecificRequest()
31 {
32 Console.WriteLine("我是三個孔的插頭");
33 }
34 }
35
36 /// <summary>
37 /// 適配器類,接口要放在類的后面,在此無法適配更多的對象,這是類適配器的不足
38 /// </summary>
39 public class ThreeToTwoAdapter:ThreeHoleAdaptee,ITwoHoleTarget
40 {
41 /// <summary>
42 /// 實現(xiàn)2個孔插頭接口方法
43 /// </summary>
44 public void Request()
45 {
46 // 調(diào)用3個孔插頭方法
47 this.SpecificRequest();
48 }
49 }
50 }
?? 代碼都很簡答,誰都可以看得懂,也有詳細的備注。
三、適配器模式的實現(xiàn)要點: ?? ? ????? 1、Adapter模式主要應(yīng)用于“希望復(fù)用一些現(xiàn)存的類,但是接口又與復(fù)用環(huán)境要求不一致的情況”,在遺留代碼復(fù)用、類庫遷移等方面非常有用。
2、GoF23定義了兩種Adapter模式的實現(xiàn)結(jié)構(gòu):對象適配器和類適配器。類適配器采用“多繼承”的實現(xiàn)方式,在C#語言中,如果被適配角色是類,Target的實現(xiàn)只能是接口,因為C#語言只支持接口的多繼承的特性。在C#語言中類適配器也很難支持適配多個對象的情況,同時也會帶來了不良的高耦合和違反類的職責單一的原則,所以一般不推薦使用。對象適配器采用“對象組合”的方式,更符合松耦合精神,對適配的對象也沒限制,可以一個,也可以多個,但是,使得重定義Adaptee的行為較困難,這就需要生成Adaptee的子類并且使得Adapter引用這個子類而不是引用Adaptee本身。Adapter模式可以實現(xiàn)的非常靈活,不必拘泥于GoF23中定義的兩種結(jié)構(gòu)。例如,完全可以將Adapter模式中的“現(xiàn)存對象”作為新的接口方法參數(shù),來達到適配的目的。
3、Adapter模式本身要求我們盡可能地使用“面向接口的編程”風(fēng)格,這樣才能在后期很方便地適配。
????? 適配器模式用來解決現(xiàn)有對象與客戶端期待接口不一致的問題,下面詳細總結(jié)下適配器兩種形式的優(yōu)缺點。
???? 1】、類的適配器模式:
???????? 優(yōu)點:
?????????????? (1)、可以在不修改原有代碼的基礎(chǔ)上來復(fù)用現(xiàn)有類,很好地符合 “開閉原則”
?????????????? (2)、可以重新定義Adaptee(被適配的類)的部分行為,因為在類適配器模式中,Adapter是Adaptee的子類
?????????????? (3)、僅僅引入一個對象,并不需要額外的字段來引用Adaptee實例(這個即是優(yōu)點也是缺點)。
???????? 缺點:
?????????????? (1)、用一個具體的Adapter類對Adaptee和Target進行匹配,當如果想要匹配一個類以及所有它的子類時,類的適配器模式就不能勝任了。因為類的適配器模式中沒有引入Adaptee的實例,光調(diào)用this.SpecificRequest方法并不能去調(diào)用它對應(yīng)子類的SpecificRequest方法。
?????????????? (2)、采用了 “多繼承”的實現(xiàn)方式,帶來了不良的高耦合。
??????? 2】、對象的適配器模式
???????????? 優(yōu)點:
????????????????? (1)、可以在不修改原有代碼的基礎(chǔ)上來復(fù)用現(xiàn)有類,很好地符合 “開閉原則”(這點是兩種實現(xiàn)方式都具有的)
????????????????? (2)、采用 “對象組合”的方式,更符合松耦合。
??????????? 缺點:
????????????????? (1)、使得重定義Adaptee的行為較困難,這就需要生成Adaptee的子類并且使得Adapter引用這個子類而不是引用Adaptee本身。
???? 3】、適配器模式使用的場景:
????????????? (1)、系統(tǒng)需要復(fù)用現(xiàn)有類,而該類的接口不符合系統(tǒng)的需求
????????????? (2)、想要建立一個可重復(fù)使用的類,用于與一些彼此之間沒有太大關(guān)聯(lián)的一些類,包括一些可能在將來引進的類一起工作。
????????????? (3)、對于對象適配器模式,在設(shè)計里需要改變多個已有子類的接口,如果使用類的適配器模式,就要針對每一個子類做一個適配器,而這不太實際。
四、.NET 中適配器模式的實現(xiàn)
??? 說道適配器模式在Net中的實現(xiàn)就很多了,比如:System.IO里面的很多類都有適配器的影子,當我們操作文件的時候,其實里面調(diào)用了COM的接口實現(xiàn)。以下兩點也是適配器使用的案例:
????? 1.在.NET中復(fù)用COM對象:
? COM對象不符合.NET對象的接口,使用tlbimp.exe來創(chuàng)建一個Runtime Callable Wrapper(RCW)以使其符合.NET對象的接口,COM Interop就好像是COM和.NET之間的一座橋梁。
2..NET數(shù)據(jù)訪問類(Adapter變體):
各種數(shù)據(jù)庫并沒有提供DataSet接口,使用DbDataAdapter可以將任何個數(shù)據(jù)庫訪問/存取適配到一個DataSet對象上,DbDataAdapter在數(shù)據(jù)庫和DataSet之間做了很好的適配。當然還有SqlDataAdapter類型了,針對微軟SqlServer類型的數(shù)據(jù)庫在和DataSet之間進行適配。
來源:http://www./content-4-36351.html
|