一 引言
設計模式就是一種思想,一種設計方式,他可以幫助我們對於一些問題(不一定是技術,如生活中的問題)的處理提供一些思路,以及問題解決方案的建模與描述
例如我們今天要講的介面卡模式就是從解決生活問題,過渡到了技術層面的問題
現實生活中,常常會出現兩者不能相容的一種情況,例如出國了,我們從國內帶的充電插頭與外國的插孔就不匹配,再例如我去香港旅遊,但是又不會講粵語,在一些小一些的商店買東西,就需要一個會講粵語的朋友幫我在中間翻譯
換到技術層面中,我們開發某些業務的部分元件已經在庫中存在了,但是由於過去開發元件時與當前介面規範不相容,使用介面卡模式,也就是找一箇中間量,幫助我們適配兩者,對接起來,這樣就不用再開發一個新的元件了
二 程式碼演示
例子背景是這樣的,我們現在有一臺蘋果手機(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)類:它是一個轉換器,通過繼承或引用適配者的物件,把適配者介面轉換成目標介面,讓客戶按目標介面的格式訪問適配者