前言
適配器模式在我們的開發(fā)中使用率極高,從代碼中隨處可見的Adapter就可以判斷出來。從最早的ListView、GridView到現(xiàn)在最新的RecyclerView都需要使用Adapter。
說到底,適配器是將兩個不兼容的類融合在一起,它有點(diǎn)像粘合劑,將不同的東西通過一種轉(zhuǎn)換使得它們能夠協(xié)作起來。例如,經(jīng)常碰到要在兩個沒有關(guān)系的類型之間進(jìn)行交互,第一個解決方案是修改各自類的接口,但是如果沒有源代碼或者我們不愿意為了一個應(yīng)用而修改各自的接口,此時怎么辦?這種情況我們往往會使用一個Adapter,在這兩種接口之間創(chuàng)建一個“混血兒”接口,這個Adapter會將這兩個接口進(jìn)行兼容,在不修改原有代碼的情況下滿足需求。
定義
適配器模式把一個類的接口變換成客戶端所期待的另一種接口,從而使原本因接口不匹配而無法在一起工作的兩個類能夠在一起工作。
使用場景
- 系統(tǒng)需要使用現(xiàn)有的類,而此類的接口不符合系統(tǒng)的需要,即接口不兼容。
- 想要建立一個可以重復(fù)使用的類,用于與一些彼此之間沒有太大關(guān)聯(lián)的一些類,包括一些可能在將來引進(jìn)的類一起工作。
- 需要一個統(tǒng)一的輸出接口,而輸入端的類型不可預(yù)知。
UML類圖
適配器模式也分兩種,即類適配器模式和對象適配器模式,首先學(xué)習(xí)類適配器模式,結(jié)構(gòu)圖如下:

如圖中所示,類適配器是通過實現(xiàn)Target接口以及繼承Adaptee類來實現(xiàn)接口轉(zhuǎn)換,例如,目標(biāo)接口需要的是operation2,但是Adaptee對象只有一個operation3,因此就出現(xiàn)了不兼容的情況。此時通過Adapter實現(xiàn)一個operation2函數(shù)將Adaptee的operation3轉(zhuǎn)換為Target需要的operation2,以此實現(xiàn)兼容。
角色介紹:
Target:目標(biāo)角色,也就是所期待得到的接口。注意:由于這里討論的是類適配器模式,因此目標(biāo)不可以是類。
Adaptee:現(xiàn)在需要適配的接口。
Adapter:適配器角色,也是本模式的核心。適配器把源接口轉(zhuǎn)換成目標(biāo)接口。顯然,這一角色不可以是接口,而必須是具體類。
簡單示例
用電源接口做例子,筆記本電腦的電源一般都是用5V電壓,但是我們生活中的電線電壓一般都是220V。這個時候就出現(xiàn)了不匹配的狀況,在軟件開發(fā)中我們稱之為接口不兼容,此時就需要適配器來進(jìn)行一個接口轉(zhuǎn)換。在軟件開發(fā)中有一句話正好體現(xiàn)了這點(diǎn):任何問題都可以加一個中間層來解決。這個層我們可以理解為這里的Adapter層,通過這層來進(jìn)行一個接口轉(zhuǎn)換就達(dá)到了兼容的目的。
在上述電源接口這個示例中,5V電壓就是Target接口,220V電壓就是Adaptee類,而將電壓從220V轉(zhuǎn)換到5V就是Adapter。
類適配器模式
// Target角色
public interface FiveVolt {
public int getVolt5();
}
// Adaptee角色,需要被轉(zhuǎn)換的對象
public class Volt220 {
public int getVolt220() {
return 220;
}
}
// Adapter角色,將220V的電壓轉(zhuǎn)換成5V的電壓
public class VoltAdapter extends Volt220 implements FiveVolt {
@Override
public int getVolt5() {
return 5;
}
}
Target角色給出了需要的目標(biāo)接口,而Adaptee類則是需要被轉(zhuǎn)換的對象。Adapter則是將Volt220轉(zhuǎn)換成Target的接口。對應(yīng)的Target的目標(biāo)是要獲取5V的輸出電壓,而Adaptee正常輸出電壓是220V,此時就需要電源適配器類將220V的電壓轉(zhuǎn)換為5V電壓,解決接口不兼容的問題。
public class Test {
public static void main(String[] args) {
VoltAdapter adapter = new VoltAdapter();
System.out.println("輸出電壓 : " + adapter.getVolt5());
}
}
對象適配器模式
與類的適配器模式一樣,對象的適配器模式把被適配的類的API轉(zhuǎn)換成為目標(biāo)類的API,與類的適配器模式不同的是,對象的適配器模式不是使用繼承關(guān)系連接到Adaptee類,而是使用代理關(guān)系連接到Adaptee類,UML圖如圖所示。

從圖中可以看出,Adaptee類(Volt220)并沒有g(shù)etVolt5()方法,而客戶端則期待這個方法。為使客戶端能夠使用Adaptee類,需要提供一個包裝類Adapter。這個包裝類包裝了一個Adaptee的實例,從而此包裝類能夠把Adaptee的API與Target類的API銜接起來。Adapter與Adaptee是委派關(guān)系,這決定了適配器模式是對象的。示例代碼如下。
/** * Target角色 */
public interface FiveVolt {
public int getVolt5();
}
/** * Adaptee角色,需要被轉(zhuǎn)換的對象 */
public class Volt220 {
public int getVolt220() {
return 220;
}
}
// 對象適配器模式
public class VoltAdapter implements FiveVolt {
Volt220 mVolt220;
public VoltAdapter(Volt220 adaptee) {
mVolt220 = adaptee;
}
public int getVolt220() {
return mVolt220.getVolt220();
}
@Override
public int getVolt5() {
return 5;
}
}
使用示例如下:
public class Test {
public static void main(String[] args) {
VoltAdapter adapter = new VoltAdapter(new Volt220());
System.out.println("輸出電壓 : " + adapter.getVolt5());
}
}
總結(jié)
優(yōu)點(diǎn):
- 系統(tǒng)需要使用現(xiàn)有的類,而此類的接口不符合系統(tǒng)的需要。那么通過適配器模式就可以讓這些功能得到更好的復(fù)用。
- 在實現(xiàn)適配器功能的時候,可以調(diào)用自己開發(fā)的功能,從而自然地擴(kuò)展系統(tǒng)的功能。
缺點(diǎn):
過多地使用適配器,會讓系統(tǒng)非常零亂,不易整體把握。例如,明明看到調(diào)用的是A接口,其實內(nèi)部被適配成了B接口的實現(xiàn),一個系統(tǒng)如果太多出現(xiàn)這種情況,無異于一場災(zāi)難。因此,如果不是很有必要,可以不使用適配器,而是直接對系統(tǒng)進(jìn)行重構(gòu)。