從今天開始咱們開始講【結構型】設計模式,【結構型】設計模式有以下幾種:適配器模式、橋接模式、裝飾模式、組合模式、外觀模式、享元模式、代理模式。【建立型】的設計模式解決的是對象建立的問題,那【結構型】設計模式解決的是類和對象的組合關係的問題。今天咱們就開始講【結構型】設計模式裏面的第一個設計模式,中文名稱:適配器模式,英文名稱:Adapter Pattern。提及這個模式其實很簡單,在現實生活中也有不少實例,好比:咱們手機的充電器,充電器的接頭,有的是把兩相電轉換爲三相電的,固然也有把三相電轉換成兩相電的。咱們常用筆記本電腦,筆記本電腦的工做電壓和咱們家裏照明電壓是不一致的,固然也就須要充電器把照明電壓轉換成筆記本的工做電壓,只有這樣筆記本電腦才能夠正常工做。太多了,就不一一列舉了。咱們只要記住一點,適配就是轉換,把不能在一塊兒工做的兩樣東西經過轉換,讓他們能夠在一塊兒工做。
2、適配器模式的詳細介紹
2.一、動機(Motivate)
在軟件系統中,因爲應用環境的變化,經常須要將「一些現存的對象」放在新的環境中應用,可是新環境要求的接口是這些現存對象所不知足的。如何應對這種「遷移的變化」?如何既能利用現有對象的良好實現,同時又能知足新的應用環境所要求的接口?
2.二、意圖(Intent)
將一個類的接口轉換成客戶但願的另外一個接口。Adapter模式使得本來因爲接口不兼容而不能一塊兒工做的那些類能夠一塊兒工做。 --《設計模式》Gof
2.三、結構圖(Structure)
適配器有兩種結構
1】、-對象適配器(更經常使用)
對象適配器使用的是對象組合的方案,它的Adapter核Adaptee的關係是組合關係。
OO中優先使用組合模式,組合模式不適用再考慮繼承。由於組合模式更加鬆耦合,而繼承是緊耦合的,父類的任何改動都要致使子類的改動。
2】、-類適配器
2.四、模式的組成
能夠看出,在適配器模式的結構圖有如下角色:
(1)、目標角色(Target):定義Client使用的與特定領域相關的接口。
(2)、客戶角色(Client):與符合Target接口的對象協同。
(3)、被適配角色(Adaptee):定義一個已經存在並已經使用的接口,這個接口須要適配。
(4)、適配器角色(Adapte) :適配器模式的核心。它將對被適配Adaptee角色已有的接口轉換爲目標角色Target匹配的接口。對Adaptee的接口與Target接口進行適配.
2.5 適配器模式的具體實現
因爲適配器模式有兩種實現結構,今天咱們針對每種都實現了本身的方式。
一、對象的是適配器模式實現
javascript
1 namespace 對象的適配器模式 2 { 3 ///<summary> 4 ///家裏只有兩個孔的插座,也懶得買插線板了,還要花錢,可是個人手機是一個有3個小柱子的插頭,明顯直接搞不定,那就適配吧 5 ///</summary> 6 class Client 7 { 8 static void Main(string[] args) 9 { 10 //好了,如今就能夠給手機充電了 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聯繫起來 46 private ThreeHoleAdaptee threeHoleAdaptee = new ThreeHoleAdaptee(); 47 //這裏能夠繼續增長適配的對象。。 48 49 /// <summary> 50 /// 實現2個孔插頭接口方法 51 /// </summary> 52 public override void Request() 53 { 54 //能夠作具體的轉換工做 55 threeHoleAdaptee.SpecificRequest(); 56 //能夠作具體的轉換工做 57 } 58 } 59 }
二、類的適配器模式實現
java
1 namespace 設計模式之適配器模式 2 { 3 /// <summary> 4 /// 這裏手機充電器爲例,咱們的家的插座是兩相電的,可是手機的插座接頭是三相電的 5 /// </summary> 6 class Client 7 { 8 static void Main(string[] args) 9 { 10 //好了,如今能夠充電了 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 /// 實現2個孔插頭接口方法 43 /// </summary> 44 public void Request() 45 { 46 // 調用3個孔插頭方法 47 this.SpecificRequest(); 48 } 49 } 50 }
代碼都很簡答,誰均可以看得懂,也有詳細的備註。
3、適配器模式的實現要點:
一、Adapter模式主要應用於「但願複用一些現存的類,可是接口又與複用環境要求不一致的狀況」,在遺留代碼複用、類庫遷移等方面很是有用。
二、GoF23定義了兩種Adapter模式的實現結構:對象適配器和類適配器。類適配器採用「多繼承」的實現方式,在C#語言中,若是被適配角色是類,Target的實現只能是接口,由於C#語言只支持接口的多繼承的特性。在C#語言中類適配器也很難支持適配多個對象的狀況,同時也會帶來了不良的高耦合和違反類的職責單一的原則,因此通常不推薦使用。對象適配器採用「對象組合」的方式,更符合鬆耦合精神,對適配的對象也沒限制,能夠一個,也能夠多個,可是,使得重定義Adaptee的行爲較困難,這就須要生成Adaptee的子類而且使得Adapter引用這個子類而不是引用Adaptee自己。Adapter模式能夠實現的很是靈活,沒必要拘泥於GoF23中定義的兩種結構。例如,徹底能夠將Adapter模式中的「現存對象」做爲新的接口方法參數,來達到適配的目的。
三、Adapter模式自己要求咱們儘量地使用「面向接口的編程」風格,這樣才能在後期很方便地適配。
適配器模式用來解決現有對象與客戶端期待接口不一致的問題,下面詳細總結下適配器兩種形式的優缺點。
1】、類的適配器模式:
優勢:
(1)、能夠在不修改原有代碼的基礎上來複用現有類,很好地符合 「開閉原則」
(2)、能夠從新定義Adaptee(被適配的類)的部分行爲,由於在類適配器模式中,Adapter是Adaptee的子類
(3)、僅僅引入一個對象,並不須要額外的字段來引用Adaptee實例(這個便是優勢也是缺點)。
缺點:
(1)、用一個具體的Adapter類對Adaptee和Target進行匹配,當若是想要匹配一個類以及全部它的子類時,類的適配器模式就不能勝任了。由於類的適配器模式中沒有引入Adaptee的實例,光調用this.SpecificRequest方法並不能去調用它對應子類的SpecificRequest方法。
(2)、採用了 「多繼承」的實現方式,帶來了不良的高耦合。
2】、對象的適配器模式
優勢:
(1)、能夠在不修改原有代碼的基礎上來複用現有類,很好地符合 「開閉原則」(這點是兩種實現方式都具備的)
(2)、採用 「對象組合」的方式,更符合鬆耦合。
缺點:
(1)、使得重定義Adaptee的行爲較困難,這就須要生成Adaptee的子類而且使得Adapter引用這個子類而不是引用Adaptee自己。
3】、適配器模式使用的場景:
(1)、系統須要複用現有類,而該類的接口不符合系統的需求
(2)、想要創建一個可重複使用的類,用於與一些彼此之間沒有太大關聯的一些類,包括一些可能在未來引進的類一塊兒工做。
(3)、對於對象適配器模式,在設計裏須要改變多個已有子類的接口,若是使用類的適配器模式,就要針對每個子類作一個適配器,而這不太實際。
4、.NET 中適配器模式的實現
說道適配器模式在Net中的實現就不少了,好比:System.IO裏面的不少類都有適配器的影子,當咱們操做文件的時候,其實裏面調用了COM的接口實現。如下兩點也是適配器使用的案例:
1.在.NET中複用COM對象:
COM對象不符合.NET對象的接口,使用tlbimp.exe來建立一個Runtime Callable Wrapper(RCW)以使其符合.NET對象的接口,COM Interop就好像是COM和.NET之間的一座橋樑。
2..NET數據訪問類(Adapter變體):
各類數據庫並無提供DataSet接口,使用DbDataAdapter能夠將任何個數據庫訪問/存取適配到一個DataSet對象上,DbDataAdapter在數據庫和DataSet之間作了很好的適配。固然還有SqlDataAdapter類型了,針對微軟SqlServer類型的數據庫在和DataSet之間進行適配。
數據庫