Adapter 介面卡模式簡介與 C# 範例【結構型1】【設計模式來了_6】

2023-08-30 06:00:56

〇、簡介

1、什麼是介面卡模式?

一句話解釋:

  兩個無關聯的類,通過實現同一介面或繼承對方得到新的介面卡類,新的介面卡類中通過實現原本類的操作,可達到進行相同的操作的目的。

介面卡模式(Apapter Pattern)是一種結構型設計模式,用於將一個類的實現轉換成使用者端所期望的另一個類,這個類中的操作和目標類具有相同的操作,以達到兩個不相關的類可以互操作的目的。

官方意圖:將一個類的介面轉換成客戶希望的另外一個介面。使得原本由於介面不相容而不能一起工作的那些類可以一起工作。 

 一個比喻:(兩位來自不同方言區的學生)

  分別來自廣州和海口的兩位同學,如果都各自說自己的家鄉話粵語和海南話,則根本無法溝通,此時普通話就是一個介面卡,將兩種方言翻譯成普通話來進行適配,這樣就可以順暢溝通了。

2、優缺點和使用場景

優缺點:

  • 通過介面卡模式,可以使兩個不相容的介面協同工作,避免了修改現有程式碼的需要。
  • 提高了程式碼的複用性和靈活性,因為介面卡可以重複使用,並且可以在不同的場景中使用。
  • 降低了系統的耦合度,介面卡模式允許系統中的各個元件相互獨立地演化。
  • 由於引入了介面卡類,可能造成系統的複雜度增加。
  • 在某些情況下,可能需要建立多個介面卡來滿足不同的使用者端需求。

適用場景:

  • 當需要使用一個已經存在的類,但其介面與你的需求不相容時,可以使用介面卡模式。
  • 當想要建立一個可複用的類,該類與一些不相關或不可預見的類進行互動時,介面卡模式也是很有用的。
  • 當希望通過一個統一的介面與多個類進行互動時,介面卡模式可以提供一個統一的介面。
  • 當需要在不破壞現有程式碼結構的情況下,對已有的類進行功能擴充套件時,可以考慮使用介面卡模式。

實際使用場景舉例:

  • 舊系統與新系統的相容:當需要將一箇舊系統的介面適配成一個新系統可以使用的介面時,介面卡模式非常有用。通過建立一個介面卡類,可以使新系統能夠無縫地與老舊系統進行通訊,而不需要修改新系統的程式碼。
  • 第三方元件的整合:當我們需要使用某個第三方元件,但其提供的介面與我們的系統需求不一致時,可以使用介面卡模式。介面卡可以將第三方元件的介面轉換為符合我們系統需求的介面形式,從而能夠順利地整合到我們的系統中。
  • 多個類庫之間的互操作:當我們需要在多個類庫之間進行互操作,但它們之間的介面不相容時,介面卡模式可以起到橋樑的作用。通過建立介面卡類,將不同類庫的介面轉換為統一的介面,實現它們之間的互操作性。
  • 介面的標準化:當系統中存在多個類似但介面不同的元件時,可以使用介面卡模式將它們的介面標準化。通過介面卡,這些元件可以統一使用相同的介面,從而提高系統的一致性和可維護性。
  • 對已有類的功能擴充套件:當我們需要對一個已有的類進行功能擴充套件時,可以使用介面卡模式。通過建立介面卡類,將新功能與原有類進行適配,使得原有類能夠使用新功能,同時不影響原有程式碼的穩定性。

一、通過範例簡單實現

 下面是一個使用介面卡設計模式的範例,假設我們有一個電源插座介面 ISocket,其中定義了供電方法 SupplyPower():

class Program // 測試
{
    static void Main(string[] args)
    {
        // 國標插座
        ChinaSocket chinaSocket = new ChinaSocket();
        ChinaSocketAdapter chinaAdapter = new ChinaSocketAdapter(chinaSocket);
        Laptop laptop1 = new Laptop(chinaAdapter); // 筆電1 通過國標版插座充電
        laptop1.Charge();
        // 美版插座
        USASocket usaSocket = new USASocket();
        USASocketAdapter usaAdapter = new USASocketAdapter(usaSocket);
        Laptop laptop2 = new Laptop(usaAdapter); // 同一型別的筆電2 通過美標版插座充電
        laptop2.Charge();
        Console.ReadKey();
    }
}
// 電源插座介面
public interface ISocket
{
    void SupplyPower();
}
// 國標版插口
public class ChinaSocket
{
    public string name { get; set; }
    public void Charge() 
    {
        Console.WriteLine("開始充電!");
    }
}
// 美版插口
public class USASocket
{
    public string name { get; set; }
    public void PowerUp() 
    {
        Console.WriteLine("Start charging!");
    }
}
// 國標版的介面卡,實現統一的電源插座介面
public class ChinaSocketAdapter : ISocket
{
    private readonly ChinaSocket _chinaSocket;
    public ChinaSocketAdapter(ChinaSocket chinaSocket)
    {
        _chinaSocket = chinaSocket;
    }
    public void SupplyPower() // 國標版實現充電方法,呼叫特有的 Charge() 方法
    {
        _chinaSocket.Charge();
    }
}
// 美版插座介面卡,實現統一的插座介面
public class USASocketAdapter : ISocket
{
    private readonly USASocket _usaSocket;
    public USASocketAdapter(USASocket usaSocket)
    {
        _usaSocket = usaSocket;
    }
    public void SupplyPower() // 美版實現充電方法,呼叫特有的 PowerUp() 方法
    {
        _usaSocket.PowerUp();
    }
}
// 筆記型電腦類實現,統一充電方法 Charge()
public class Laptop
{
    private readonly ISocket _socket;
    public Laptop(ISocket socket)
    {
        _socket = socket;
    }
    public void Charge()
    {
        _socket.SupplyPower();
    }
}

 最終,無論是國標還是美版的插座,均呼叫 Charge() 方法進行充電,充當介面卡角色的就是電源插座介面 IScoket。

二、介面卡模式的結構

如下簡單畫一下上一章節範例程式碼對應的類圖:(美版相似就省略了,只顯示國標版)

Client:給 Laptop 宣告插座並完成充電動作。

Laptop:定義統一充電介面的模板,供介面卡實現。

ChinaSocketAdapter:對 ChinaSocket 類和 Laptop 進行適配。

ChinaSocket:已存在的類,此類需要適配。

三、介面卡模式在 .Net Core 中的實際應用

IDataAdapter 介面的實現類通常是資料庫存取器,它們提供了一種與特定資料庫型別無關的方式來存取資料。

下面是介面 IDataAdapter 的原始碼:

// System.Data.Common, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
// System.Data.IDataAdapter
using System.Data;
using System.Diagnostics.CodeAnalysis;

public interface IDataAdapter
{
	MissingMappingAction MissingMappingAction { get; set; }

	MissingSchemaAction MissingSchemaAction { get; set; }

	ITableMappingCollection TableMappings { get; }

	[RequiresUnreferencedCode("IDataReader's (built from adapter commands) schema table types cannot be statically analyzed.")]
	DataTable[] FillSchema(DataSet dataSet, SchemaType schemaType);

	int Fill(DataSet dataSet);

	IDataParameter[] GetFillParameters();

	[RequiresUnreferencedCode("IDataReader's (built from adapter commands) schema table types cannot be statically analyzed.")]
	int Update(DataSet dataSet);
}

 具體的資料庫,均實現了 IDataAdapter 介面:

// SQL Server
public sealed class SqlDataAdapter : DbDataAdapter, IDataAdapter, IDbDataAdapter, ICloneable
{ ... }
// Oracle
[Designer("Oracle.VsDevTools.OracleVSGDataAdapterWizard, Oracle.VsDevTools, Version=4.122.1.0, Culture=neutral, PublicKeyToken=89b483f429c47342, processorArchitecture=MSIL", typeof(IDesigner))]
[ToolboxBitmap(typeof(resfinder), "Oracle.ManagedDataAccess.src.Client.Icons.OracleDataAdapterToolBox_hc.bmp")]
[DefaultEvent("RowUpdated")]
public sealed class OracleDataAdapter : DbDataAdapter, IDbDataAdapter, IDataAdapter
{ ... }
// MySQL
[DesignerCategory("Code")]
[Designer("MySql.Data.MySqlClient.Design.MySqlDataAdapterDesigner,MySqlClient.Design")]
public sealed class MySqlDataAdapter : DbDataAdapter, IDbDataAdapter, IDataAdapter
{ ... }

通過使用 IDataAdapter 介面,我們可以編寫與特定資料庫型別無關的程式碼,而只需要關心資料集的結構和操作。這樣,我們就可以更輕鬆地更換或擴充套件我們的資料來源,而不需要修改應用程式的程式碼。同時,IDataAdapter 介面還支援資料對映和資料驗證等高階功能,使得我們可以更好地處理複雜的資料操作。

四、相關模式

橋接模式(Bridge)的結構與物件介面卡類似,但是 Bridge 模式的出發點不同。Bridge 的目的是將介面部分和實現部分分離,從而可以對它們較為容易也相對獨立地加以改變。而 Adapter  則意味著改變一個已有物件的介面。

裝飾模式(Decorator)增強了其他物件的功能而同時又不改變它的介面,因此 Decorator 對應用程式的透明性比介面卡要好。結果是 Decorator 支援遞迴組合,而純粹使用介面卡是不可能實現這一點的。

代理模式(Proxy)在不改變它的介面的條件下,為另一個物件定義了一個代理。