首頁 > 軟體

.Net結構型設計模式之介面卡模式(Adapter)

2022-05-25 18:01:13

一、動機(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)的文章就介紹到這了。希望對大家的學習有所幫助,也希望大家多多支援it145.com。


IT145.com E-mail:sddin#qq.com