【設計模式】第六篇:來康康介面卡模式

BWH_Steven發表於2020-11-15

一 引言

設計模式就是一種思想,一種設計方式,他可以幫助我們對於一些問題(不一定是技術,如生活中的問題)的處理提供一些思路,以及問題解決方案的建模與描述

例如我們今天要講的介面卡模式就是從解決生活問題,過渡到了技術層面的問題

現實生活中,常常會出現兩者不能相容的一種情況,例如出國了,我們從國內帶的充電插頭與外國的插孔就不匹配,再例如我去香港旅遊,但是又不會講粵語,在一些小一些的商店買東西,就需要一個會講粵語的朋友幫我在中間翻譯

換到技術層面中,我們開發某些業務的部分元件已經在庫中存在了,但是由於過去開發元件時與當前介面規範不相容,使用介面卡模式,也就是找一箇中間量,幫助我們適配兩者,對接起來,這樣就不用再開發一個新的元件了

二 程式碼演示

例子背景是這樣的,我們現在有一臺蘋果手機(Lightning 介面),但是我們現在只有一根 Type-C 的充電線,我們需要想辦法,增加一箇中間的轉接器,從而能達到給手機充電的效果

(一) 類介面卡(繼承,不常用)

(1) 演示

雖然這一種不常用,但是第二種就是在第一種基礎上改進的,內容不是很多,可以耐心看完喔

首先,定義一個蘋果手機類,其中有一個充電的方法(Converter 在後面有說,接著往下看)

/**
 * 客戶端類:蘋果手機想充電,但是充電線的頭是 Type-C 的
 */
public class Phone {
    // 充電方法
    public void charging(Converter converter){
        converter.TypeCToLightning();
    }
}

接著定義的就是 Type-C 這條充電線,因為它是沒辦法插入蘋果手機的 Lightning 介面 的,所以它就是被適配的類

/**
 * 被適配的類:Type-C充電線
 */
public class TypeCLine {

    public void charging(){
        System.out.println("開始充電了");
    }
}

兩個不能直接聯絡的內容,我們已經定義好了,即手機和 Type-C 充電線,現在就差一箇中間的介面卡了,現在可以定義一個轉換的介面,其中定義一個Type-C 轉為 Lightning 的方法

/**
 * 充電器轉換介面
 */
public interface Converter {
    // Type-C 轉為 蘋果 Lightning 介面
    void TypeCToLightning();
}

接下來就是一個具體的實現類了,實現抽象介面沒什麼好說的,還有一步是關鍵——繼承充電線這個需要被適配的類,至於它的利弊我們下面再說

/**
 * Type-C 充電線轉化器
 */
public class TypeCLineAdapter extends TypeCLine implements Converter {
    @Override
    public void TypeCToLightning() {
        // 轉接成功,可以使用經過轉接後的 type-C 的線充蘋果手機了
        super.charging(); 
    }
}

測試一下

public class Test {
    public static void main(String[] args) {
        Phone phone = new Phone(); // 手機
        TypeCLine typeCLine = new TypeCLine(); // Type-C充電線
        Converter typeCLineAdapter = new TypeCLineAdapter(); // Type-C充電線轉接器
        // 手機直接引入轉接器從而充電
        phone.charging(typeCLineAdapter);
    }
}

結果:開始充電了

(2) 利弊

測試程式碼中,大家應該也能看出來了 typeCLine 好像並沒有被用到,確實如此,因為我們在介面卡類,即 TypeCLineAdapter 類中,是通過直接繼承 TypeCLine 這個 Type-C 充電線類的,也就是說,它的理念是通過多重繼承來實現兩個介面之間進行匹配,但是像 C#,VB.NET,Java等語言都是不支援多繼承的,而且其使用了繼承,繼承在控制不恰當的情況下總會給我們帶來一些麻煩,所以在這裡也並不是很合適,所以就有了下面的方式

(二) 物件介面卡(組合,常用)

(1) 演示

手機和 Type-C 充電線這是不變的,需要做的就是修改具體的介面卡類 TypeCLineAdapter,我們這裡建立一個新的 TypeCLineAdapter2

/**
 * Type-C 充電線轉化器
 */
public class TypeCLineAdapter2 implements Converter {

    private TypeCLine typeCLine;

    public TypeCLineAdapter2(TypeCLine typeCLine) {
        this.typeCLine = typeCLine;
    }

    @Override
    public void TypeCToLightning() {
        // 轉接成功,可以使用經過轉接後的 type-C 的線充蘋果手機了
        typeCLine.charging(); 
    }
}

測試一下:

public class Test {
    public static void main(String[] args) {
        Phone phone = new Phone(); // 手機
        TypeCLine typeCLine = new TypeCLine(); // Type-C充電線
        Converter typeCLineAdapter2 = new TypeCLineAdapter2(typeCLine); // Type-C充電線轉接器
        // 手機直接引入轉接器從而充電
        phone.charging(typeCLineAdapter2);
    }
}

(2) 利弊

在 TypeCLineAdapter2 的步驟中,首先我們取消了介面卡繼承 TypeCLine 類,而是選擇通過在介面卡TypeCLineAdapter2 這個類 中增加一個 TypeCLine 的私有域,然後再在方法中呼叫

首先,我們擺脫了單繼承帶來的煩惱,那麼這樣做還有什麼好處嗎

在 《Effecttive Java》一書中,第 4 章 第 18 條:複合優先於繼承中有提到,有一個比較好的說法,我簡單摘了一下:

我們可以建立一個新的類(介面卡 TypeCLineAdapter2 類),在新的類中新增一個私有域(TypeCLine 類),他引用現有類的一個例項,這種設計叫做複合

  • 因此現有的類變成了新類的一個元件,新類中的每個例項方法都可以呼叫被包含的現有的類中對應的方法,並返回他的結果,這就是轉發,而新類中的方法被稱為 轉發方法

小結:這種方式下,測試的時候感覺也會更舒服,和我們想的一般,手機和 Type-C 的線,分別通過連結介面卡,就達到了能適配充電的方式,同樣又避免了使用繼承,算是一種比較好的適配問題處理方式

三 介面卡模式理論

(一) 定義和分類

介面卡模式(Adapter)定義:將一個類的介面轉換成客戶希望的另外一個介面,使得原本由於介面不相容而不能一起工作的那些類能一起工作

分類:

  • 類介面卡模式

    • 主要使用繼承實現,耦合度高,且在單繼承的語言中使用會受限,還需要防止繼承帶來的一些問題
  • 物件介面卡模式

    • 使用了組合(或叫複合)的方式降低了耦合,推薦使用

(二) 結構

① 類介面卡模式

② 物件介面卡模式

還是依舊分析一下其中的角色:

適配者(Adaptee)類:它是需要被適配的類,例如上面提到的 Type-C 線,適配後才能插入到蘋果手機中充電

目標(Target)介面:當前系統業務所期待的介面,它可以是抽象類或介面,對應程式碼中的 Converter 介面,他是客戶期待的適配轉換介面

介面卡(Adapter)類:它是一個轉換器,通過繼承或引用適配者的物件,把適配者介面轉換成目標介面,讓客戶按目標介面的格式訪問適配者

相關文章