.Net結構型設計模式之適配器模式(Adapter)

一、動機(Motivation)

在軟件系統中,由於應用環境的變化,常常需要將“一些現存的對象”放在新的環境中應用,但是新環境要求的接口是這些現存對象所不滿足的。

如何應對這種“遷移的變化”?如何既能利用現有對象的良好實現,同時又能滿足新的應用環境所要求的接口?

二、意圖(Intent)

將一個類的接口轉換成客戶希望的另一個接口。Adapter模式使得原本由於接口不兼容而不能一起工作的那些類可以一起工作。

例說Adapter應用

這種實際上是一種委派的調用,本來是發送請求給MyStack,但是MyStack實際上是委派給list去處理。MyStack在這裡其實就是Adapter(適配對象),list即是Adaptee(被適配的對象),而IStack就是客戶期望的接口。太直接瞭,沒什麼可說的。

三、結構(Structure)

適配器有兩種結構

1、對象適配器(更常用)

對象適配器使用的是對象組合的方案,它的Adapter和Adaptee的關系是組合關系,即上面例子中MyStack和list是組合關系。

OO中優先使用組合模式,組合模式不適用再考慮繼承。因為組合模式更加松耦合,而繼承是緊耦合的,父類的任何改動都要導致子類的改動。

上面的例子就是對象適配器。

2、類適配器(不推薦使用)

下面的例子是類適配器。

Adapter繼承瞭ArrayList,也繼承瞭IStack接口,它既可以使用ArrayList裡的方法,也可以使用IStack接口裡的方法,這樣就感覺有點不倫不類。這個類違反瞭類應該具有單一職責的原則,它既有ArrayList的職責,也有IStack的職責,因此這種類適配不是很常用,也不推薦使用。

註意:如果一個方法有可能要委托到2個或2個以上的對象,或者2個或2個以上的類需要委托,對於對象適配器,隻需要增加幾個內部的屬性就可以實現適配。

而對於類適配器,因為C#中類隻能是單一繼承,它不能繼承自2個或2個以上的類,所以類適配器這裡便無法使用。

四、模式的組成

可以看出,在適配器模式的結構圖有以下角色:
(1)、目標角色(Target):定義Client使用的與特定領域相關的接口。
(2)、客戶角色(Client):與符合Target接口的對象協同。
(3)、被適配角色(Adaptee):定義一個已經存在並已經使用的接口,這個接口需要適配。
(4)、適配器角色(Adapte) :適配器模式的核心。它將對被適配Adaptee角色已有的接口轉換為目標角色Target匹配的接口。對Adaptee的接口與Target接口進行適配。

五、 適配器模式的具體實現

實現一個對棧的操作,有一個IStact接口,裡面有三個方法Push(進棧)、Pop(出棧)和GetTopItem(取最頂層元素),這個IStact接口將相當於上面的Target,想要實現進棧出棧的操作,如果自己去實現數據結構顯得比較麻煩,在此可以將net提供的ArrayList類拿來一用,ArrayList類就是被適配的對象,相當於上面的Adaptee。在寫一個適配類StactAdapter類完成功能就可以瞭。

/// <summary>
/// 棧的接口
/// </summary>
public interface IStack
{
    void Push(object item);
    void Pop();
    Object GetTopItem();
}
/// <summary>
/// 對象適配器
/// </summary>
public class StactAdapter : IStack
{
    ArrayList list;
    /// <summary>
    /// 構造函數中實例化ArrayList
    /// </summary>
    public StactAdapter()
    {
        list = new ArrayList();
    }
    /// <summary>
    /// 進棧
    /// </summary>
    /// <param name="item">壓入棧的元素</param>
    public void Push(object item)
    {
        list.Add(item);
    }
    /// <summary>
    /// 出棧
    /// </summary>
    public void Pop()
    {
        list.RemoveAt(list.Count - 1);
    }
    /// <summary>
    /// 取最頂層的元素
    /// </summary>
    /// <returns></returns>
    public Object GetTopItem()
    {
        return list[list.Count - 1];
    }
}
/// <summary>
/// 客戶調用
/// </summary>
public class App
{
    static void Main(string[] args)
    {
        IStack myStack = new StactAdapter();
        myStack.Push("oec2003");
        myStack.Push("oec2004");
        myStack.Push("oec2005");
        myStack.Pop();
        Console.WriteLine(myStack.GetTopItem());
    }
}

六、適配器模式的實現要點:

1、Adapter模式主要應用於“希望復用一些現存的類,但是接口又與復用環境要求不一致的情況”,在遺留代碼復用、類庫遷移等方面非常有用。
2、GoF23定義瞭兩種Adapter模式的實現結構:對象適配器和類適配器。類適配器采用“多繼承”的實現方式,在C#語言中,如果被適配角色是類,Target的實現隻能是接口,因為C#語言隻支持接口的多繼承的特性。在C#語言中類適配器也很難支持適配多個對象的情況,同時也會帶來瞭不良的高耦合和違反類的職責單一的原則,所以一般不推薦使用。對象適配器采用“對象組合”的方式,更符合松耦合精神,對適配的對象也沒限制,可以一個,也可以多個,但是,使得重定義Adaptee的行為較困難,這就需要生成Adaptee的子類並且使得Adapter引用這個子類而不是引用Adaptee本身。Adapter模式可以實現的非常靈活,不必拘泥於GoF23中定義的兩種結構。例如,完全可以將Adapter模式中的“現存對象”作為新的接口方法參數,來達到適配的目的。
3、Adapter模式本身要求我們盡可能地使用“面向接口的編程”風格,這樣才能在後期很方便地適配。
適配器模式用來解決現有對象與客戶端期待接口不一致的問題,下面詳細總結下適配器兩種形式的優缺點。

1、類的適配器模式:

優點:

(1)、可以在不修改原有代碼的基礎上來復用現有類,很好地符合 “開閉原則”
(2)、可以重新定義Adaptee(被適配的類)的部分行為,因為在類適配器模式中,Adapter是Adaptee的子類
(3)、僅僅引入一個對象,並不需要額外的字段來引用Adaptee實例(這個即是優點也是缺點)。
缺點:

(1)、用一個具體的Adapter類對Adaptee和Target進行匹配,當如果想要匹配一個類以及所有它的子類時,類的適配器模式就不能勝任瞭。因為類的適配器模式中沒有引入Adaptee的實例,光調用this.SpecificRequest方法並不能去調用它對應子類的SpecificRequest方法。
(2)、采用瞭 “多繼承”的實現方式,帶來瞭不良的高耦合。

2、對象的適配器模式

 優點:

(1)、可以在不修改原有代碼的基礎上來復用現有類,很好地符合 “開閉原則”(這點是兩種實現方式都具有的)
(2)、采用 “對象組合”的方式,更符合松耦合。
缺點:

使得重定義Adaptee的行為較困難,這就需要生成Adaptee的子類並且使得Adapter引用這個子類而不是引用Adaptee本身。

3、適配器模式使用的場景:

(1)、系統需要復用現有類,而該類的接口不符合系統的需求
(2)、想要建立一個可重復使用的類,用於與一些彼此之間沒有太大關聯的一些類,包括一些可能在將來引進的類一起工作。
(3)、對於對象適配器模式,在設計裡需要改變多個已有子類的接口,如果使用類的適配器模式,就要針對每一個子類做一個適配器,而這不太實際。

七、.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之間進行適配。

到此這篇關於.Net結構型設計模式之適配器模式(Adapter)的文章就介紹到這瞭。希望對大傢的學習有所幫助,也希望大傢多多支持WalkonNet。

推薦閱讀: