介面卡模式:如何讓不相容的介面變得相容

2023-09-08 18:00:46

在軟體開發中,我們經常會遇到這樣的情況:我們需要使用一個現有的類或者介面,但它與我們系統的目標介面不相容,而我們又不能修改它。這時候,我們該怎麼辦呢?大多數情況下我們都可以使用介面卡模式來解決這個問題,本文將從以下四個方面講解介面卡模式

  • 簡介
  • 優缺點
  • 應用場景
  • Java 程式碼範例

簡介

介面卡模式(Adapter Pattern)是一種結構型設計模式,它可以將一個介面轉換成使用者端所期待的另一個介面,從而使原本由於介面不相容而不能一起工作的類可以一起工作。介面卡模式也稱為包裝器模式(Wrapper Pattern),因為它通過一個包裝類(即介面卡)來包裝不相容的介面,並提供統一的目標介面。介面卡模式可以在執行時根據需要選擇不同的介面卡來適配不同的被適配者。

物件介面卡模式的各角色定義如下。

  • Target(目標介面):使用者端要使用的目標介面標準,對應下文中的三相插孔介面 TriplePin。
  • Adapter(介面卡):實現了目標介面,負責適配(轉換)被適配者的介面 specificRequest()為目標介面 request(),對應本章下文中的電視機專屬介面卡類 TriplePinAdapter。
  • Adaptee(被適配者):被適配者的介面標準,目前不能相容目標介面的問題介面,可以有多種實現類,對應下文中的兩相插孔介面 DualPin。
  • Client(使用者端):目標介面的使用者。

推薦博主開源的 H5 商城專案waynboot-mall,這是一套全部開源的微商城專案,包含三個專案:運營後臺、H5 商城前臺和伺服器端介面。實現了商城所需的首頁展示、商品分類、商品詳情、商品 sku、分詞搜尋、購物車、結算下單、支付寶/微信支付、收單評論以及完善的後臺管理等一系列功能。 技術上基於最新得 Springboot3.0、jdk17,整合了 MySql、Redis、RabbitMQ、ElasticSearch 等常用中介軟體。分模組設計、簡潔易維護,歡迎大家點個 star、關注博主。

github 地址:https://github.com/wayn111/waynboot-mall

優缺點

介面卡模式的優點有:

  • 介面卡模式可以增強程式的可延伸性,通過使用介面卡,可以在不修改原有程式碼的基礎上引入新的功能或者介面。
  • 介面卡模式可以提高類的複用性,通過使用介面卡,可以將已有的類或者介面重新組合和封裝,使其符合新的需求。
  • 介面卡模式可以增加類的透明度,通過使用介面卡,使用者端只需要關注目標介面,而無需瞭解被適配者的具體實現。
  • 介面卡模式可以靈活地切換不同的被適配者,通過使用不同的介面卡,可以動態地選擇不同的被適配者來滿足不同的場景。

介面卡模式的缺點有:

  • 介面卡模式會增加系統的複雜性,過多地使用介面卡會使系統變得零亂和難以理解。
  • 介面卡模式可能會降低系統的效能,因為每次呼叫目標介面時都需要經過介面卡的轉換。
  • 介面卡模式可能會違反開閉原則,如果目標介面發生變化,則需要修改所有的介面卡類。

應用場景

介面卡模式適用於以下場景:

  • 當需要在一個已有系統中引入新的功能或者介面時,它與系統的目標介面不相容,但又不能修改原有程式碼時,可以使用介面卡模式。例如在一個資料庫作業系統中,如果想要支援多種型別的資料庫源,但系統只提供了一個固定型別資料庫源的操作介面時,可以使用一個資料庫源操作介面卡來將不同型別資料庫源轉換成統一型別資料庫源。
  • 當需要在多個獨立開發的系統或者元件之間進行共同作業時,但由於各自採用了不同的介面或者協定時,可以使用介面卡模式。例如在一個分散式服務系統中,如果想要讓不同語言編寫的服務之間進行通訊和呼叫,但各自採用了不同的通訊協定和資料格式時,可以使用一個服務通訊介面卡來將不同協定和資料格式轉換成統一協定和資料格式。

Java 程式碼範例

舉一個生活中常見的範例,我們新買了一臺電視機,其電源插頭是兩相的,不巧的是牆上的插孔卻是三相的,這時電視機便無法通電使用,我們以程式碼來重現這個場景。

  1. 定義目標介面:三相插口 TriplePin,其中 3 個引數 l、n、e 分別對應火線(live)、零線(null)和地線(earth)。
public interface TriplePin {

    public void electrify(int l, int n, int e);
}
  1. 定義被適配者介面:兩項插口 DualPin,可以看到引數中缺少了地線 e 引數。
public interface DualPin {

    public void electrify(int l, int n);
}

  1. 新增被適配者介面具體實現類:TV,可以看到 TV 實現的是兩相介面,所在無法直接在三項介面中使用。
public class TV implements DualPin {
    @Override
    public void electrify(int l, int n) {
        System.out.println("火線通電:" + l + ",零線通電:" + n);
        System.out.println("電視開機");
    }
}

  1. 定義介面卡類:三項介面介面卡 TriplePinAdapter,實現了三項介面並且包含兩項介面屬性,在 electrify 方法中呼叫被適配裝置的兩插通電方法,忽略地線引數 e,以此來完成三項介面對兩項介面的相容。

這也就意味著 TriplePinAdapter 類能幫助我們將 TV 類與三項介面相容。

public class TriplePinAdapter implements TriplePin {

    private DualPin dualPin;

    public TriplePinAdapter(DualPin dualPin) {
        this.dualPin = dualPin;
    }

    @Override
    public void electrify(int l, int n, int e) {
        // 呼叫被適配裝置的兩插通電方法,忽略地線引數e
        dualPin.electrify(l, n);
    }
}
  1. 定義使用者端類
public class Client {

    public static void main(String[] args) {
        DualPin dualPinDevice = new TV();
        TriplePin triplePinDevice = new TriplePinAdapter(dualPinDevice);
        triplePinDevice.electrify(1, 0, -1);
    }
}

輸出結果如下:

火線通電:1,零線通電:0
電視開機

總結

通過利用介面卡模式對系統進行擴充套件後,我們就不必再為解決相容性問題去暴力修改類介面了,轉而通過介面卡,以更為優雅、巧妙的方式將兩側「對立」的介面「整合」在一起,順利化解雙方難以調和的矛盾,最終使它們順利接通。

關注公眾號【waynblog】每週分享技術乾貨、開源專案、實戰經驗、國外優質文章翻譯等,您的關注將是我的更新動力!