1. 程式人生 > 實用技巧 >設計模式:介面卡模式(類介面卡、物件介面卡、介面介面卡)

設計模式:介面卡模式(類介面卡、物件介面卡、介面介面卡)


介面卡模式的工作原理:

將一個類的介面轉換為另一種介面,讓原本介面不相容的類可以相容

從使用者的角度是看不到介面卡的,是解耦的;使用者呼叫的是介面卡轉化後的目標介面方法,介面卡再呼叫被適配者的相關介面方法。(比如使用者只用type-C,要的是這個口給出的5v電壓,而介面卡去插插孔),這樣對於使用者來說,只是目標和介面互動。


一、類介面卡模式


類介面卡會有一個 Adapter 類,通過繼承 src(被適配者) 類,實現 dst(目標) 介面,完成從 src -> dst 的適配。

這幾個之間的關係:

比如充電器是 Adapter , 220V 交流電是 src ,5V直流電是 dst。也就是說,目標是要插 dst ,但是隻有 src 是源,src 需要被適配,src 是被適配者,介面卡是 Adapter。

如上類圖所示:

  1. Phone 和介面 Dest 關聯,為了的得到 5V 電壓,和介面關聯更加適合擴充套件,Dest就是我們的 dst;
  2. Src220V就是我們的src,提供220V的交流電,需要被適配;
  3. Adapter就是我們說的Adapter,完成把 src 轉換為 dst 的功能,他實現了 dst 介面,繼承了 src 類。
/*
    被適配者
*/
public class Src220V {
    public int output220V(){
        int src = 220;
        System.out.println("電壓 = "  + src + "V");
        return src;
    }
}
/*
    目標dst,5V電壓,是介面
*/
public interface Dest5V {
     public int output5V();
}
/*
    類介面卡Adapter,繼承被適配類src,實現dst
*/
public class Adapter extends Src220V implements Dest5V{
    @Override
    public int output5V() {
        //獲取到源電壓
        int src = output220V();
        //轉成目標電壓
        int dst = src / 44;
        return dst;
    }
}
/*
    手機直接依賴的是介面卡Adapter,但是是通過Dest5V介面依賴的
*/
public class Phone {
    //充電
    public void chongdian(Dest5V dest5V){
        //這裡其實肯定是 5
        if (dest5V.output5V() == 5)
        System.out.println("電壓為 5 V,可以正常充電");
    }
}

那麼簡單的測試就可以寫一個客戶端:

public class Client {
    public static void main(String[] args) {
        System.out.println("=== 類介面卡模式 ===");
        Phone phone = new Phone();
        phone.chongdian(new Adapter());
    }
}

總結:

  1. 因為 java 是單繼承機制,所以類介面卡 Adapter 需要繼承 src ,算是一個缺點(一直強調儘量不要繼承)。因為這個要求 dst 必須是介面,不能讓 Adapter 同時繼承兩個類,有一定侷限性;
  2. src 的方法都會在 Adapter中暴露出來,增加了使用成本;
  3. 優點:因為 Adapter 繼承了 src 類,所以可以根據需求重寫 src 類的方法,使得 Adapter 的靈活性增強。

二、物件介面卡模式


物件介面卡模式的基本思路和類介面卡是相同的,只是將 Adapter 類稍作修改,不是繼承 src 類,而是持有一個 src 的例項,解決相容性的問題,即:持有 src 類,實現 dst 介面,完成 src -> dst 的適配。

根據設計模式的第七個原則:合成複用原則,儘量不要繼承,而用別的替代,這就是物件介面卡改進的思路。

和上一種 類介面卡模式 一樣,用手機充電的例子來修改程式碼,看一下類圖的改變:

src 不再是被繼承,而是以一個例項的方式出現在 Adapter 裡,其他地方做相應的改變即可。

  1. 被適配者 :不變;
  2. 目標dst : 不變;
  3. 介面卡 Adapter:改變
/*
    物件介面卡Adapter,聚合一個被適配類src物件,實現dst
*/
public class Adapter implements Dest5V{
    //聚合一個物件
    private Src220V src220V;
    //提供構造器
    public Adapter(Src220V src220V) {
        this.src220V = src220V;
    }

    @Override
    public int output5V() {
        int dst = 0;
        if (src220V != null){
            //獲取到源電壓
            int src = src220V.output220V();
            System.out.println("=== 使用物件介面卡 ===");
            dst = src / 44;
        }
        return dst;
    }
}
  1. 使用者Phone :不變;

Client:響應改變,呼叫方式變了,需要 src 物件。

public class Client {
    public static void main(String[] args) {
        System.out.println("=== 物件介面卡模式 ===");
        Phone phone = new Phone();
        phone.chongdian(new Adapter(new Src220V()));
    }
}

總結:

和類介面卡是一種思想,不過根據合成複用原則,將 Adapter 必須繼承 src 的侷限性問題優化,變成聚合一個物件。


三、介面介面卡模式


一些書也把介面介面卡模式叫做 : 預設介面卡模式,或者介面卡模式。

當不需要全部實現介面提供的方法的時候,可以先設計一個抽象類來實現介面(都實現成一個空方法),那麼接著抽象類的子類就可以有選擇的覆蓋父類的方法來實現需求。適用於一個介面不想使用其所有的方法的情況。

對於上面的例子,可以不用直接實現 dst 這個介面,而是改成實現一個 AbstractAdapter,都實現成空方法,這樣的話,繼續讓 Phone 依賴抽象類,就能用匿名內部類的方式重寫某些方法。

改動比較小,因此就不做程式碼示例了。


四、介面卡模式應用


在 SpringMVC 框架裡,原始碼的 HanlderAdapter 就使用了介面卡模式。