標籤:lin src 橋接模式 wrap 結果 logs readline 用戶端 http
一、引言
從今天開始我們開始講【結構型】設計模式,【結構型】設計模式有如下幾種:適配器模式、橋接模式、裝飾模式、組合模式、面板模式、享元模式、代理模式。【建立型】的設計模式解決的是對象建立的問題,那【結構型】設計模式解決的是類和對象的組合關係的問題。今天我們就開始講【結構型】設計模式裡面的第一個設計模式,適配器模式。說起這個模式其實很簡單,在現實生活中也有很多執行個體,比如:我們手機的充電器,充電器的接頭,有的是把兩相電轉換為三相電的,當然也有把三相電轉換成兩相電的。我們經常使用膝上型電腦,膝上型電腦的工作電壓和我們家裡照明電壓是不一致的,當然也就需要充電器把照明電壓轉換成筆記本的工作電壓,只有這樣膝上型電腦才可以正常工作。太多了,就不一一列舉了。我們只要記住一點,適配就是轉換,把不能在一起工作的兩樣東西通過轉換,讓他們可以在一起工作。
二、適配器模式的詳細介紹
2.1、動機(Motivate)
在軟體系統中,由於應用環境的變化,常常需要將“一些現存的對象”放在新的環境中應用,但是新環境要求的介面是這些現存對象所不滿足的。如何應對這種“遷移的變化”?如何既能利用現有對象的良好實現,同時又能滿足新的應用環境所要求的介面?
2.2、意圖(Intent)
將一個類的介面轉換成客戶希望的另一個介面。Adapter模式使得原本由於介面不相容而不能一起工作的那些類可以一起工作。
2.3、結果圖
適配器有兩種結構
-對象適配器(更常用)
對象適配器使用的是對象組合的方案,它的Adapter核Adaptee的關係是組合關係。
OO中優先使用組合模式,組合模式不適用再考慮繼承。因為組合模式更加松耦合,而繼承是緊耦合的,父類的任何改動都要導致子類的改動。
-類適配器
2.4、模式的組成
. 目標角色(Target):定義Client使用的與特定領域相關的介面。
. 客戶角色(Client):與符合Target介面的對象協同。
. 被適配角色(Adaptee):定義一個已經存在並已經使用的介面,這個介面需要適配。
. 適配器角色(Adapte) :適配器模式的核心。它將對被適配Adaptee角色已有的介面轉換為目標角色Target匹配的介面。對Adaptee的介面與Target介面進行適配.
2.5 適配器模式的具體實現
由於適配器模式有兩種實現結構,今天我們針對每種都實現了自己的方式。
1、對象的是適配器模式實現
1 namespace 對象的適配器模式 2 { 3 ///<summary> 4 ///家裡只有兩個孔的插座,也懶得買插線板了,還要花錢,但是My Phone是一個有3個小柱子的插頭,明顯直接搞不定,那就適配吧 5 ///</summary> 6 class Client 7 { 8 static void Main(string[] args) 9 {10 //好了,現在就可以給手機充電了11 TwoHole homeTwoHole = new ThreeToTwoAdapter();12 homeTwoHole.Request();13 Console.ReadLine();14 }15 }16 17 /// <summary>18 /// 我家只有2個孔的插座,也就是適配器模式中的目標(Target)角色,這裡可以寫成抽象類別或者介面19 /// </summary>20 public class TwoHoleTarget21 {22 // 用戶端需要的方法23 public virtual void Request()24 {25 // 可以把一般實現放在這裡26 }27 }28 29 /// <summary>30 /// 手機充電器是有3個柱子的插頭,源角色——需要適配的類(Adaptee)31 /// </summary>32 public class ThreeHoleAdaptee33 {34 public void SpecificRequest()35 {36 Console.WriteLine("我是3個孔的插頭");37 }38 }39 40 /// <summary>41 /// 適配器類,TwoHole這個對象寫成介面或者抽象類別更好,面向介面編程嘛42 /// </summary>43 public class ThreeToTwoAdapter : TwoHoleTarget44 {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 }
2、類的適配器模式實現
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 ITwoHoleTarget21 {22 void Request();23 }24 25 /// <summary>26 /// 3個孔的插頭,源角色——需要適配的類(Adaptee)27 /// </summary>28 public abstract class ThreeHoleAdaptee29 {30 public void SpecificRequest()31 {32 Console.WriteLine("我是三個孔的插頭");33 }34 }35 36 /// <summary>37 /// 適配器類,介面要放在類的後面,在此無法適配更多的對象,這是類適配器的不足38 /// </summary>39 public class ThreeToTwoAdapter:ThreeHoleAdaptee,ITwoHoleTarget40 {41 /// <summary>42 /// 實現2個孔插頭介面方法43 /// </summary>44 public void Request()45 {46 // 調用3個孔插頭方法47 this.SpecificRequest();48 }49 }50 }
代碼都很簡答,誰都可以看得懂,也有詳細的備忘。
三、適配器模式的實現要點:
Adapter模式主要應用於“希望複用一些現存的類,但是介面又與複用環境要求不一致的情況”,在遺留代碼複用、類庫遷移等方面非常有用。
GoF23定義了兩種Adapter模式的實現結構:對象適配器和類適配器。類適配器採用“多繼承”的實現方式,在C#語言中,如果被適配角色是類,Target的實現只能是介面,因為C#語言只支援介面的多繼承的特性。在C#語言中類適配器也很難支援適配多個對象的情況,同時也會帶來了不良的高耦合和違反類的職責單一的原則,所以一般不推薦使用。對象適配器採用“對象組合”的方式,更符合松耦合精神,對適配的對象也沒限制,可以一個,也可以多個,但是,使得重定義Adaptee的行為較困難,這就需要產生Adaptee的子類並且使得Adapter引用這個子類而不是引用Adaptee本身。Adapter模式可以實現的非常靈活,不必拘泥於GoF23中定義的兩種結構。例如,完全可以將Adapter模式中的“現存對象”作為新的介面方法參數,來達到適配的目的。
Adapter模式本身要求我們儘可能地使用“面向介面的編程”風格,這樣才能在後期很方便地適配。
適配器模式用來解決現有對象與用戶端期待介面不一致的問題,下面詳細總結下適配器兩種形式的優缺點。
四、.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之間進行適配。
五、總結
今天的文章就寫到這裡了,在結束今天寫作之前,有一句話還是要說的,雖然以前說過。每種設計模式都有自己的適用情境,它是為瞭解決一類問題,沒有所謂的缺點,沒有一種設計模式可以解決所有情況的。我們使用設計模式的態度是通過不斷地重構來使用模式,不要一上來就使用設計模式,為了模式而模式。如果軟體沒有需求的變化,我們不使用模式都沒有問題。遇到問題,我們就按著常規來寫,有了需求變化,然後我們去抽象,瞭解使用的情境,然後在選擇合適的設計模式。
C#設計模式之七適配器模式(Adapter)【結構型】