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