本筆記摘抄自:https://www.cnblogs.com/PatrickLiu/p/7640873.html,記錄一下學習程序以備后續查用,
一、引言
從今天開始我們開始講結構型設計模式,結構型設計模式有如下幾種:配接器模式、橋接模式、裝飾模式、組合模式、外觀模式、享元模式、代理模式,
創建型設計模式解決的是物件創建的問題,而結構型設計模式解決的是類和物件組合關系的問題,
今天我們開始講結構型設計模式里面的第一個設計模式:配接器模式,配接器模式其實很簡單,在現實生活中有很多這樣的實體實體:比如,手機充電器的
接頭是二插的,假如只有三插的插座,就必須通過三插轉二插的轉換器才可以正常充電;筆記本電腦的作業電壓和家庭照明的電壓是不一致的,需要通過變壓
器(俗稱火牛)才能讓筆記本電腦正常作業,配接器的例子數不勝數,只需記住一點:適配就是轉換,讓不能在一起作業的兩樣東西通過轉換可以正常作業,
二、配接器模式介紹
配接器模式:英文名稱--Adapter Pattern;分類--結構型,
2.1、動機(Motivate)
在軟體系統中,由于應用環境的變化,常常需要將“一些現存的物件”放在新的環境中應用,但是新的環境要求的介面是這些現存物件所不能滿足的,如何應
對這種“遷移的變化”?如何既能利用現有物件的良好實作,同時又能滿足新的應用環境所要求的介面?
2.2、意圖(Intent)
將一個類的介面轉換成客戶希望的另一個介面,Adapter模式使得原本由于介面不兼容而不能一起作業的那些類可以一起作業,--《設計模式》Gof
2.3、結構圖(Structure)
配接器有兩種結構:
1)物件配接器(更常用)

物件配接器使用的是物件組合的方案,它的Adapter和Adaptee的關系是組合關系,
OO中優先使用組合模式,組合模式不適用時再考慮繼承,因為組合模式更加松耦合,而繼承是緊耦合的,父類的任何改動都要導致子類的改動,
2)類配接器

2.4、模式的組成
從上兩圖可以看出,在配接器模式的結構圖有以下角色:
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 } }View Code
運行結果如下:

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 } }View Code
運行結果如下:
三、配接器模式的實作要點
1)Adapter模式主要應用于“希望復用一些現存的類,但是介面又與復用環境要求不一致的情況”,在遺留代碼復用、類別庫遷移等方面非常有用,
2)GoF23定義了兩種Adapter模式的實作結構:物件配接器和類配接器,類配接器采用“多繼承”的實作方式,在C#語言中,如果被適配角色是類,Target的
實作只能是介面,因為C#語言只支持介面的多繼承,在C#語言中類配接器也很難支持適配多個物件的情況,同時也會帶來了不良的高耦合和違反類的單一職
責的原則,所以一般不推薦使用,物件配接器采用“物件組合”的方式,更符合松耦合精神,對適配的物件也沒限制,可以一個也可以多個,但是,這也使得重
定義Adaptee的行為比較困難,這就需要生成Adaptee的子類并且使得Adapter參考這個子類而不是參考Adaptee本身,Adapter模式可以實作的非常靈活,不必
拘泥于GoF23中定義的兩種結構,例如,完全可以將Adapter模式中的“現存物件”作為新的介面方法引數,來達到適配的目的,
3)Adapter模式本身要求我們盡可能地使用“面向介面的編程”風格,這樣才能在后期很方便地適配,
下面詳細總結下配接器兩種形式的優缺點:
3.1、物件配接器模式
優點:
1)可以在不修改原有代碼的基礎上來復用現有類,很好地符合 “開閉原則”,
2)采用 “物件組合”的方式,更符合松耦合,
缺點:
1)使得重定義Adaptee的行為較困難,這就需要生成Adaptee的子類并且使得Adapter參考這個子類而不是參考Adaptee本身,
3.2、類配接器模式
優點:
1)可以在不修改原有代碼的基礎上來復用現有類,很好地符合 “開閉原則”,
2)可以重新定義Adaptee(被適配的類)的部分行為,因為在類配接器模式中,Adapter是Adaptee的子類,
3)僅僅引入一個物件,并不需要額外的欄位來參考Adaptee實體(這個即是優點也是缺點),
缺點:
1)用一個具體的Adapter類對Adaptee和Target進行匹配,當如果想要匹配一個類以及所有它的子類時,類的配接器模式就不能勝任了,因為類的配接器模
式中沒有引入Adaptee的實體,光呼叫SpecificRequest方法并不能去呼叫它對應子類的SpecificRequest方法,
2)采用了 “多繼承”的實作方式,帶來了不良的高耦合,
3.3、配接器模式的使用場景
1)系統需要復用現有類,而該類的介面不符合系統的需求,
2)想要建立一個可重復使用的類,用于與一些彼此之間沒有太大關聯的一些類,包括一些可能在將來引進的類一起作業,
3)對于物件配接器模式,在設計里需要改變多個已有子類的介面,如果使用類的配接器模式,就要針對每一個子類做一個配接器,而這不太實際,
四、.NET中配接器模式的實作
說到配接器模式在.Net中的實作就很多了,比如:System.IO里面的很多類都有配接器的影子,當我們操作檔案的時候,其實里面呼叫了COM的介面實作,
以下兩點也是配接器使用的案例:
4.1、.NET中復用COM物件
COM物件不符合.NET物件的介面,使用tlbimp.exe來創建一個Runtime Callable Wrapper(RCW)以使其符合.NET物件的介面,COM Interop就好像是
COM和.NET之間的一座橋梁,
4.2、.NET資料訪問類(Adapter變體)
各種資料庫并沒有提供DataSet介面,使用DbDataAdapter可以將任何資料庫訪問/存取適配到一個DataSet物件上,DbDataAdapter在資料庫和DataSet之間
做了很好的適配,當然還有SqlDataAdapter型別,針對微軟SqlServer型別的資料庫在和DataSet之間進行適配,
五、總結
有一句話還是要說的,雖然以前說過,每種設計模式都有自己的適用場景,它是為了解決一類問題,沒有所謂的缺點,沒有一種設計模式可以解決所有情況
的,我們使用設計模式的態度是通過不斷地重構來使用模式,不要一上來就使用設計模式,為了模式而模式,如果軟體沒有需求的變化,我們不使用模式都沒
有問題,遇到問題,我們就按著常規來寫,有了需求變化,然后我們去抽象,了解使用的場景,然后再選擇合適的設計模式,
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/77381.html
標籤:C#
