設計模式:介面卡模式(類介面卡、物件介面卡、介面介面卡)
介面卡模式的工作原理:
將一個類的介面轉換為另一種介面,讓原本介面不相容的類可以相容。
從使用者的角度是看不到介面卡的,是解耦的;使用者呼叫的是介面卡轉化後的目標介面方法,介面卡再呼叫被適配者的相關介面方法。(比如使用者只用type-C,要的是這個口給出的5v電壓,而介面卡去插插孔),這樣對於使用者來說,只是目標和介面互動。
一、類介面卡模式
類介面卡會有一個 Adapter 類,通過繼承 src(被適配者) 類,實現 dst(目標) 介面,完成從 src -> dst 的適配。
這幾個之間的關係:
比如充電器是 Adapter , 220V 交流電是 src ,5V直流電是 dst。也就是說,目標是要插 dst ,但是隻有 src 是源,src 需要被適配,src 是被適配者,介面卡是 Adapter。
如上類圖所示:
- Phone 和介面 Dest 關聯,為了的得到 5V 電壓,和介面關聯更加適合擴充套件,Dest就是我們的 dst;
- Src220V就是我們的src,提供220V的交流電,需要被適配;
- 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());
}
}
總結:
- 因為 java 是單繼承機制,所以類介面卡 Adapter 需要繼承 src ,算是一個缺點(一直強調儘量不要繼承)。因為這個要求 dst 必須是介面,不能讓 Adapter 同時繼承兩個類,有一定侷限性;
- src 的方法都會在 Adapter中暴露出來,增加了使用成本;
- 優點:因為 Adapter 繼承了 src 類,所以可以根據需求重寫 src 類的方法,使得 Adapter 的靈活性增強。
二、物件介面卡模式
物件介面卡模式的基本思路和類介面卡是相同的,只是將 Adapter 類稍作修改,不是繼承 src 類,而是持有一個 src 的例項,解決相容性的問題,即:持有 src 類,實現 dst 介面,完成 src -> dst 的適配。
根據設計模式的第七個原則:合成複用原則,儘量不要繼承,而用別的替代,這就是物件介面卡改進的思路。
和上一種 類介面卡模式 一樣,用手機充電的例子來修改程式碼,看一下類圖的改變:
src 不再是被繼承,而是以一個例項的方式出現在 Adapter 裡,其他地方做相應的改變即可。
- 被適配者 :不變;
- 目標dst : 不變;
- 介面卡 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;
}
}
- 使用者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 就使用了介面卡模式。