java設(shè)計(jì)模式—適配器模式
## 適配器模式
適配器模式是設(shè)計(jì)模式中的一種,屬于結(jié)構(gòu)型模式。這個(gè)模式的主要目標(biāo)是使得原本不兼容的接口能夠相互合作。
就像現(xiàn)實(shí)生活中的電源適配器一樣,可以使得不同的電源插頭可以在不同的電源插座上使用。
## 主要解決的問題
在軟件開發(fā)過程中,經(jīng)常會(huì)遇到兩個(gè)已有的類,它們的功能滿足我們的需求,但是它們的接口并不兼容。
如果直接修改這兩個(gè)類的源代碼,可能會(huì)引入新的錯(cuò)誤,或者破壞已有的測(cè)試。
在這種情況下,我們可以使用適配器模式,來(lái)提供一個(gè)能夠調(diào)和這兩個(gè)類接口差異的解決方案。
## 何時(shí)使用適配器模式
- 你想使用一個(gè)已經(jīng)存在的類,但是它的接口不符合你的需求。
- 你想創(chuàng)建一個(gè)可以復(fù)用的類,該類可以與其他不相關(guān)的類或者不可預(yù)見的類(即那些接口可能不一定兼容的類)協(xié)同工作。
- 你需要使用幾個(gè)已有的子類,但是不可能對(duì)每一個(gè)都進(jìn)行子類化以匹配它們的接口。對(duì)象適配器可以適配它的父接口。
## 舉一個(gè)生活中的例子
想象你去了一個(gè)外國(guó)國(guó)家,你帶了一個(gè)電動(dòng)剃須刀,但是你發(fā)現(xiàn)那個(gè)國(guó)家的電源插頭和你的剃須刀插頭不兼容。這時(shí),你需要一個(gè)電源適配器,它一頭適配你的剃須刀插頭,另一頭適配那個(gè)國(guó)家的電源插座。
在這個(gè)例子中,電源適配器就扮演了“適配器”的角色。
## 優(yōu)點(diǎn)
- 提高了類的復(fù)用性。
- 增加了類的透明度。
- 靈活性好。
## 缺點(diǎn)
- 過多地使用適配器,會(huì)使系統(tǒng)非常零亂,不易整體進(jìn)行把握。比如,明明看到調(diào)用的是A接口,其實(shí)內(nèi)部被適配成了B接口的實(shí)現(xiàn),一個(gè)系統(tǒng)的復(fù)雜性往往來(lái)自于這些難以直視的細(xì)節(jié)。
## 使用場(chǎng)景
在軟件系統(tǒng)中,當(dāng)你想使用某個(gè)類,但是它的接口不符合你的需求,或者你想創(chuàng)建一個(gè)可以復(fù)用的類,該類可以與其他不相關(guān)或不可預(yù)見的類協(xié)同工作,那么可以使用適配器模式。
## 代碼示例
假設(shè)有一個(gè)MediaPlayer接口和一個(gè)更高級(jí)的AdvancedMediaPlayer接口。我們想要讓MediaPlayer可以播放更高級(jí)的音頻,但是我們不想改變現(xiàn)有的類,所以我們創(chuàng)建了一個(gè)適配器。
````
// 媒體播放器接口
public interface MediaPlayer {
? ?void play(String audioType, String fileName);
}
// 高級(jí)媒體播放器接口
public interface AdvancedMediaPlayer {? ??
? ?void playVlc(String fileName);
? ?void playMp4(String fileName);
}
// 創(chuàng)建實(shí)現(xiàn)了 AdvancedMediaPlayer 接口的實(shí)體類。
public class VlcPlayer implements AdvancedMediaPlayer{
? ?@Override
? ?public void playVlc(String fileName) {
? ? ? System.out.println("Playing vlc file. Name: "+ fileName);? ? ? ??
? ?}
? ?@Override
? ?public void playMp4(String fileName) {
? ? ? //do nothing
? ?}
}
public class Mp4Player implements AdvancedMediaPlayer{
? ?@Override
? ?public void playVlc(String fileName) {
? ? ? //do nothing
? ?}
? ?@Override
? ?public void playMp4(String fileName) {
? ? ? System.out.println("Playing mp4 file. Name: "+ fileName);? ? ? ??
? ?}
}
// 創(chuàng)建適配器類,實(shí)現(xiàn) MediaPlayer 接口
public class MediaAdapter implements MediaPlayer {
? ?AdvancedMediaPlayer advancedMusicPlayer;
? ?public MediaAdapter(String audioType){
? ?
? ? ? if(audioType.equalsIgnoreCase("vlc") ){
? ? ? ? ?advancedMusicPlayer = new VlcPlayer();? ? ? ? ? ??
? ? ??
? ? ? }else if (audioType.equalsIgnoreCase("mp4")){
? ? ? ? ?advancedMusicPlayer = new Mp4Player();
? ? ? }? ?
? ?}
? ?@Override
? ?public void play(String audioType, String fileName) {
? ?
? ? ? if(audioType.equalsIgnoreCase("vlc")){
? ? ? ? ?advancedMusicPlayer.playVlc(fileName);
? ? ? }
? ? ? else if(audioType.equalsIgnoreCase("mp4")){
? ? ? ? ?advancedMusicPlayer.playMp4(fileName);
? ? ? }
? ?}
}
// 創(chuàng)建實(shí)現(xiàn)了 MediaPlayer 接口的實(shí)體類。
public class AudioPlayer implements MediaPlayer {
? ?MediaAdapter mediaAdapter;?
? ?@Override
? ?public void play(String audioType, String fileName) {? ? ? ??
? ? ? // 播放 mp3 音樂文件的內(nèi)置支持
? ? ? if(audioType.equalsIgnoreCase("mp3")){
? ? ? ? ?System.out.println("Playing mp3 file. Name: " + fileName);? ? ? ? ? ??
? ? ? }?
? ? ??
? ? ? // mediaAdapter 提供了播放其他文件格式的支持
? ? ? else if(audioType.equalsIgnoreCase("vlc")?
? ? ? ? ?|| audioType.equalsIgnoreCase("mp4")){
? ? ? ? ?mediaAdapter = new MediaAdapter(audioType);
? ? ? ? ?mediaAdapter.play(audioType, fileName);
? ? ? }
? ? ??
? ? ? else{
? ? ? ? ?System.out.println("Invalid media. " + audioType + " format not supported");
? ? ? }
? ?}? ?
}
````
在這個(gè)例子中,AudioPlayer通過使用MediaAdapter適配器類,可以播放mp3、mp4和vlc格式的音頻,而不需要直接實(shí)現(xiàn)AdvancedMediaPlayer接口。