適配器模式
適配器在生活中無處不在,比如電腦的轉(zhuǎn)接頭、讀卡器、電源轉(zhuǎn)接頭。他們的共同點(diǎn)就是接口標(biāo)準(zhǔn)不一樣,需要通過適配器轉(zhuǎn)換后才能使用。就拿讀卡器來說,存儲卡的接口只能適配相機(jī)或者手機(jī)的卡槽。而電腦普遍為 USB 接口。那么如何在電腦上使用存儲卡呢?我們可以用讀卡器,一頭卡槽能夠插入存儲卡,另一頭 USB 可以插在電腦上。通過適配器可以解決接口不兼容的問題。還有個例子就是電腦的變壓器,電腦一般接收20V電壓,但是我國電壓是220V,因此就需要變壓器做轉(zhuǎn)換,如下圖所示,進(jìn)來是220V,出來被轉(zhuǎn)為20V。變壓器其實(shí)就是適配器。
1. 實(shí)現(xiàn)適配器模式
我們通過如下例子,來看看如何實(shí)現(xiàn)適配器模式。假如我們的電視機(jī)屏幕輸出為 4K 畫質(zhì),但播放器只能輸出 2K 的畫質(zhì),此時就需要一個適配器完成 2K 到 4K 的轉(zhuǎn)換。代碼如下:
只能輸出 2k 信號的 player
:
public class Player {
public TwoThousandSignal play() {
return new TwoThousandSignal();
}
}
我們定義一個更為現(xiàn)代的播放器的接口,輸出 4K 信號:
public interface ModernPlayer {
FourThousandSignal play();
}
這個接口的實(shí)現(xiàn)就是一個適配器( adapter ),通過復(fù)用 Player 輸出的 2K 信號,轉(zhuǎn)化為 4K 信號,讓支持 ModernPlayer
的設(shè)備來播放 2K 信號源。
public class ModernPlayerAdapter implements ModernPlayer {
private Player player = new Player();
@Override
public FourThousandSignal play() {
TwoThousandSignal twoThousandSignal = player.play();
return convertToFourThousandSignal(twoThousandSignal);
}
private FourThousandSignal convertToFourThousandSignal(TwoThousandSignal twoThousandSignal) {
//4k信號通過算法計(jì)算,從2k轉(zhuǎn)換而來。省略轉(zhuǎn)換邏輯,
return new FourThousandSignal();
}
}
電視機(jī)作為調(diào)用方,只需要使用 ModernPlayerAdapter
的實(shí)例就可以播放 2K 信號,代碼如下:
public class Television {
private ModernPlayer modernPlayer = new ModernPlayerAdapter();
public void display(){
modernPlayer.play();
}
}
看代碼是不是很像代理模式?ModernPlayerAdapter
只是調(diào)用了Adaptee的方法,獲得 2k 信號后轉(zhuǎn)換為 4K 信號。區(qū)別在于 Player
并沒有實(shí)現(xiàn) ModernPlayer
接口。而代理模式,Proxy
和 RealSubject
是都需要實(shí)現(xiàn)同一個接口的。Adapter
的作用是適配不同接口,兩個接口的返回值是不同的,Adapter
中需要實(shí)現(xiàn)轉(zhuǎn)換邏輯。
類圖:
2. 適配器模式優(yōu)點(diǎn)
-
不需要修改現(xiàn)有的接口和實(shí)現(xiàn),就能復(fù)用已有的類;
-
靈活度高,可以在接口不變的情況下,兼容多種不同的類。
3. 適配器模式適用場景
-
想要使用一個已有的類,但是此類的接口并不符合使用方要;
-
多個類做的事情相同或者類似,但是各自接口又不同。調(diào)用方希望統(tǒng)一接口。
第一個場景可以認(rèn)為是亡羊補(bǔ)牢。由于種種原因造成系統(tǒng)接口不同,但功能卻類似。此時很可能我們不能直接修改已經(jīng)存在的接口,我們只能通過適配器模式去適配這個接口。
第二個場景其實(shí)也很常見。比如我們開發(fā)一個比價(jià)網(wǎng)站,需要從不同網(wǎng)站抓取同類商品的價(jià)格,然后按照自己系統(tǒng)的數(shù)據(jù)結(jié)構(gòu)保存。不同網(wǎng)站抓取到的數(shù)據(jù)肯定是不同的,可能字段名不一樣,也可能數(shù)據(jù)結(jié)構(gòu)都不同。但是最終都要保存為同樣的數(shù)據(jù)結(jié)構(gòu),此時就需要適配器來做轉(zhuǎn)換。
4. 小結(jié)
當(dāng)我們面對難以改造,又想復(fù)用的對象時,可以考慮采用適配器模式。但切記一定不要濫用適配器。我們應(yīng)該在最初設(shè)計(jì)程序的時候就考慮代碼的可擴(kuò)展性。而不是最后通過適配器來解決問題。能修改重構(gòu)的,盡量去修改。實(shí)在不能修改的,比如外部系統(tǒng)的接口,我們就只能通過適配器模式來解決問題。