本筆記摘抄自:http://www.javashuo.com/article/p-ktjdqwpc-gh.html,記錄一下學習過程以備後續查用。html
1、引言數據庫
從今天開始咱們開始講結構型設計模式,結構型設計模式有以下幾種:適配器模式、橋接模式、裝飾模式、組合模式、外觀模式、享元模式、代理模式。編程
創建型設計模式解決的是對象建立的問題,而結構型設計模式解決的是類和對象組合關係的問題。設計模式
今天咱們開始講結構型設計模式裏面的第一個設計模式:適配器模式。適配器模式其實很簡單,在現實生活中有不少這樣的實例實例:好比,手機充電器的app
接頭是二插的,假如只有三插的插座,就必須經過三插轉二插的轉換器才能夠正常充電;筆記本電腦的工做電壓和家庭照明的電壓是不一致的,須要經過變壓ide
器(俗稱火牛)才能讓筆記本電腦正常工做。適配器的例子數不勝數,只需記住一點:適配就是轉換,讓不能在一塊兒工做的兩樣東西經過轉換能夠正常工做。學習
2、適配器模式介紹spa
適配器模式:英文名稱--Adapter Pattern;分類--結構型。設計
2.一、動機(Motivate)代理
在軟件系統中,因爲應用環境的變化,經常須要將「一些現存的對象」放在新的環境中應用,可是新的環境要求的接口是這些現存對象所不能知足的。如何應
對這種「遷移的變化」?如何既能利用現有對象的良好實現,同時又能知足新的應用環境所要求的接口?
2.二、意圖(Intent)
將一個類的接口轉換成客戶但願的另外一個接口。Adapter模式使得本來因爲接口不兼容而不能一塊兒工做的那些類能夠一塊兒工做。--《設計模式》Gof
2.三、結構圖(Structure)
適配器有兩種結構:
1)對象適配器(更經常使用)
對象適配器使用的是對象組合的方案,它的Adapter和Adaptee的關係是組合關係。
OO中優先使用組合模式,組合模式不適用時再考慮繼承,由於組合模式更加鬆耦合。而繼承是緊耦合的,父類的任何改動都要致使子類的改動。
2)類適配器
2.四、模式的組成
從上兩圖能夠看出,在適配器模式的結構圖有如下角色:
1)目標角色(Target):定義Client使用的與特定領域相關的接口。
2)客戶角色(Client):與符合Target接口的對象協同。
3)被適配角色(Adaptee):定義一個已經存在並已經使用的接口,這個接口須要適配。
4)適配器角色(Adapter) :適配器模式的核心,它將對被適配Adaptee角色已有的接口轉換爲目標角色Target匹配的接口並進行適配。
2.5 、適配器模式的具體實現
2.5.1對象適配器模式的實現
class Program { /// <summary> /// 目標角色(Target)--兩孔插座,這裏能夠寫成抽象類或者接口。 /// </summary> public class TwoHoleTarget { //客戶端須要的方法 public virtual void Request() { Console.WriteLine("我須要兩孔的插座。"); } } /// <summary> /// 源角色(Adaptee)--三孔插座,須要適配的類。 /// </summary> public class ThreeHoleAdaptee { public void SpecificRequest() { Console.WriteLine("增長三孔轉兩孔的插座,兩孔充電器也可使用了。"); } } /// <summary> /// 適配器類 /// </summary> public class ThreeToTwoAdapter : TwoHoleTarget { //建立三孔插座的實例 private ThreeHoleAdaptee threeHoleAdaptee = new ThreeHoleAdaptee(); /// <summary> /// 實現兩孔插座接口方法 /// </summary> public override void Request() { //具體的轉換工做 threeHoleAdaptee.SpecificRequest(); } } static void Main(string[] args) { #region 適配器模式之對象適配器 TwoHoleTarget twoHole = new ThreeToTwoAdapter(); twoHole.Request(); Console.ReadLine(); #endregion } }
運行結果以下:
2.5.2類適配器模式實現
class Program { /// <summary> /// 目標角色(Target)--兩孔插座,這裏只能是接口,也是類適配器的限制。 /// </summary> public interface ITarget { void Request(); } /// <summary> /// 源角色(Adaptee)--三孔插座,須要適配的類。 /// </summary> public abstract class Adaptee { public void SpecificRequest() { Console.WriteLine("增長三孔轉兩孔的插座,兩孔充電器也可使用了。"); } } /// <summary> /// 適配器類,接口要放在類的後面,在此沒法適配更多的對象,這是類適配器的不足。 /// </summary> public class Adapter : Adaptee, ITarget { /// <summary> /// 實現兩孔插座接口方法 /// </summary> public void Request() { //具體的轉換工做 SpecificRequest(); } } static void Main(string[] args) { #region 適配器模式之類適配器 ITarget twoHole = new Adapter(); twoHole.Request(); Console.ReadLine(); #endregion } }
運行結果以下:
3、適配器模式的實現要點
1)Adapter模式主要應用於「但願複用一些現存的類,可是接口又與複用環境要求不一致的狀況」,在遺留代碼複用、類庫遷移等方面很是有用。
2)GoF23定義了兩種Adapter模式的實現結構:對象適配器和類適配器。類適配器採用「多繼承」的實現方式,在C#語言中,若是被適配角色是類,Target的
實現只能是接口,由於C#語言只支持接口的多繼承。在C#語言中類適配器也很難支持適配多個對象的狀況,同時也會帶來了不良的高耦合和違反類的單一職
責的原則,因此通常不推薦使用。對象適配器採用「對象組合」的方式,更符合鬆耦合精神,對適配的對象也沒限制,能夠一個也能夠多個,可是,這也使得重
定義Adaptee的行爲比較困難,這就須要生成Adaptee的子類而且使得Adapter引用這個子類而不是引用Adaptee自己。Adapter模式能夠實現的很是靈活,沒必要
拘泥於GoF23中定義的兩種結構。例如,徹底能夠將Adapter模式中的「現存對象」做爲新的接口方法參數,來達到適配的目的。
3)Adapter模式自己要求咱們儘量地使用「面向接口的編程」風格,這樣才能在後期很方便地適配。
下面詳細總結下適配器兩種形式的優缺點:
3.一、對象適配器模式
優勢:
1)能夠在不修改原有代碼的基礎上來複用現有類,很好地符合 「開閉原則」。
2)採用 「對象組合」的方式,更符合鬆耦合。
缺點:
1)使得重定義Adaptee的行爲較困難,這就須要生成Adaptee的子類而且使得Adapter引用這個子類而不是引用Adaptee自己。
3.二、類適配器模式
優勢:
1)能夠在不修改原有代碼的基礎上來複用現有類,很好地符合 「開閉原則」。
2)能夠從新定義Adaptee(被適配的類)的部分行爲,由於在類適配器模式中,Adapter是Adaptee的子類。
3)僅僅引入一個對象,並不須要額外的字段來引用Adaptee實例(這個便是優勢也是缺點)。
缺點:
1)用一個具體的Adapter類對Adaptee和Target進行匹配,當若是想要匹配一個類以及全部它的子類時,類的適配器模式就不能勝任了。由於類的適配器模
式中沒有引入Adaptee的實例,光調用SpecificRequest方法並不能去調用它對應子類的SpecificRequest方法。
2)採用了 「多繼承」的實現方式,帶來了不良的高耦合。
3.三、適配器模式的使用場景
1)系統須要複用現有類,而該類的接口不符合系統的需求。
2)想要創建一個可重複使用的類,用於與一些彼此之間沒有太大關聯的一些類,包括一些可能在未來引進的類一塊兒工做。
3)對於對象適配器模式,在設計裏須要改變多個已有子類的接口,若是使用類的適配器模式,就要針對每個子類作一個適配器,而這不太實際。
4、.NET中適配器模式的實現
說到適配器模式在.Net中的實現就不少了,好比:System.IO裏面的不少類都有適配器的影子,當咱們操做文件的時候,其實裏面調用了COM的接口實現。
如下兩點也是適配器使用的案例:
4.一、.NET中複用COM對象
COM對象不符合.NET對象的接口,使用tlbimp.exe來建立一個Runtime Callable Wrapper(RCW)以使其符合.NET對象的接口,COM Interop就好像是
COM和.NET之間的一座橋樑。
4.二、.NET數據訪問類(Adapter變體)
各類數據庫並無提供DataSet接口,使用DbDataAdapter能夠將任何數據庫訪問/存取適配到一個DataSet對象上,DbDataAdapter在數據庫和DataSet之間
作了很好的適配。固然還有SqlDataAdapter類型,針對微軟SQL Server類型的數據庫在和DataSet之間進行適配。
5、總結
有一句話仍是要說的,雖然之前說過。每種設計模式都有本身的適用場景,它是爲了解決一類問題,沒有所謂的缺點,沒有一種設計模式能夠解決全部狀況
的。咱們使用設計模式的態度是經過不斷地重構來使用模式,不要一上來就使用設計模式,爲了模式而模式。若是軟件沒有需求的變化,咱們不使用模式都沒
有問題。遇到問題,咱們就按着常規來寫,有了需求變化,而後咱們去抽象,瞭解使用的場景,而後再選擇合適的設計模式。