適配器模式作爲一種結構型設計模式,在軟件系統設計開發中使用到的頻率非常之高,比如Java中的jdbc。本文結合具體案例講解適配器模式的知識要點。
1.適配器模式介紹
意圖:將一個類的接口轉換成功能開發需要的另一個接口。適配器模式使得原本由於接口不兼容而不能一起工作的那些類可以一起工作。
主要解決:主要解決在軟件系統中,常常要將一些"現存的對象"放到新的環境中,而新環境要求的接口是現對象不能滿足的。
何時使用: 1、系統需要使用現有的類,而此類的接口不符合系統的需要。 2、想要建立一個可以重複使用的類,用於與一些彼此之間沒有太大關聯的一些類,包括一些可能在將來引進的類一起工作,這些源類不一定有一致的接口。 3、通過接口轉換,將一個類插入另一個類系中。
如何解決:繼承或依賴(推薦)。
關鍵代碼:適配器繼承或依賴已有的對象,實現想要的目標接口。
2.案例實現
比如:我們有一個音頻播放器只能播放MP3格式的音頻文件,一個視頻播放器只能播放MP4格式的視頻文件,我們如何實現一個高級播放器對來對外提供支持MP3和MP4格式的多媒體文件呢?
定義一個 Player 接口和一個實現了Player 接口的實體類 MediaPlayer。Player接口定義一個play(MediaFile file)的接口;
定義一個 AdvancedMediaPlayer 和實現了 AdvancedMediaPlayer 接口的實體類VideoPlayer和AudioPlayer。實體類VideoPlayer可以播放MP4視頻文件,AudioPlayer可以播放MP3音頻文件;
我們想要讓 MediaPlayer 播放MP3和MP4多媒體文件。爲了實現這個功能,我們需要創建一個實現了 MediaPlayer 接口的適配器類 MediaPlayerAdapter,並使用 AdvancedMediaPlayer 對象來播放所需的格式。
MediaPlayer 使用適配器類 MediaPlayerAdapter 傳遞所需的音頻類型,不需要知道能播放所需格式音頻的實際類。AdapterModeTest,爲我們的演示類使用 MediaPlayer 類來播放各種格式。
實現步驟1:定義接口
播放器接口及高級媒體播放器接口:
Player:
package com.yx.adapter;
import com.yx.model.MediaFile;
/**
* MediaPlayer
*
* @author yx
* @date 2019/11/24 18:25
*/
public interface Player {
void play(MediaFile file);
}
AdvanceMediaPlayer:
package com.yx.player;
import com.yx.model.MediaFile;
/**
* AdvanceMediaPlayer
* 高級播放器,可以播放音頻和視頻
*
* @author yx
* @date 2019/11/24 18:01
*/
public interface AdvanceMediaPlayer {
/**
* 播放音頻文件
*
* @param file 多媒體文件
*/
void playAudio(MediaFile file);
/**
* 播放視頻文件
*
* @param file 多媒體文件
*/
void playVideo(MediaFile file);
}
實現步驟2:AdvanceMediaPlayer接口實現類
AudioPlayer:
package com.yx.player;
import com.yx.model.MediaFile;
/**
* AudioPlayer
* 音頻播放器只能播放音頻
*
* @author yx
* @date 2019/11/24 18:13
*/
public class AudioPlayer implements AdvanceMediaPlayer {
@Override
public void playAudio(MediaFile file) {
System.out.println("AudioPlayer can play audio file:" + file.getFile().getName());
}
@Override
public void playVideo(MediaFile file) {
}
}
VideoPlayer:
package com.yx.player;
import com.yx.model.MediaFile;
/**
* 視頻播放器
* 音頻播放器只能播放音頻
*
* @author yx
* @date 2019/11/24 18:13
*/
public class VideoPlayer implements AdvanceMediaPlayer {
@Override
public void playAudio(MediaFile file) {
}
@Override
public void playVideo(MediaFile file) {
System.out.println("VideoPlayer can play video file:" + file.getFile().getName());
}
}
實現步驟3:創建Player接口的適配器類
MediaPlayerAdapter:
package com.yx.adapter;
import com.yx.model.MediaFile;
import com.yx.player.AdvanceMediaPlayer;
import com.yx.player.AudioPlayer;
import com.yx.player.VideoPlayer;
/**
* MediaPlayerAdapter
* 多媒體播放器適配
*
* @author yx
* @date 2019/11/24 18:18
*/
public class MediaPlayerAdapter implements Player {
private AdvanceMediaPlayer player = null;
public MediaPlayerAdapter(String audioType) {
if (MediaFile.MEDIA_FILE_TYPE_AUDIO.equals(audioType)) {
player = new AudioPlayer();
} else if (MediaFile.MEDIA_FILE_TYPE_VIDEO.equals(audioType)) {
player = new VideoPlayer();
}
}
@Override
public void play(MediaFile file) {
if (MediaFile.MEDIA_FILE_TYPE_AUDIO.equals(file.getMediaType())) {
player.playAudio(file);
} else if (MediaFile.MEDIA_FILE_TYPE_VIDEO.equals(file.getMediaType())) {
player.playVideo(file);
} else {
System.out.println(file.getMediaType() + " format not supported");
}
}
}
實現步驟4:Player接口實現類
MediaPlayer:
package com.yx.player;
import com.yx.adapter.MediaPlayerAdapter;
import com.yx.adapter.Player;
import com.yx.model.MediaFile;
/**
* MediaPlayer
*
* @author yx
* @date 2019/11/24 18:34
*/
public class MediaPlayer implements Player {
MediaPlayerAdapter mAdapter;
@Override
public void play(MediaFile file) {
mAdapter = new MediaPlayerAdapter(file.getMediaType());
mAdapter.play(file);
}
}
實現步驟5:使用MediaPlayer 來播放不同類型的多媒體文件。
AdapterModeTest:
package com.yx.adapter;
import com.yx.model.MediaFile;
import com.yx.player.MediaPlayer;
import java.io.File;
/**
* AdapterModeTest
*
* @author yx
* @date 2019/11/24 18:50
*/
public class AdapterModeTest {
public static void main(String[] args) {
MediaFile file1 = new MediaFile("audio", new File("audio.mp3"));
MediaFile file2 = new MediaFile("video", new File("video.mp4"));
MediaFile file3 = new MediaFile("doc", new File("test.doc"));
MediaPlayer player = new MediaPlayer();
player.play(file1);
player.play(file2);
player.play(file3);
}
}
運行結果:
AudioPlayer can play audio file:audio.mp3
VideoPlayer can play video file:video.mp4
doc format not supported
在該模式下,如果需要添加其他媒體文件支持,只需要對適配器MediaPlayerAdapter和AdvanceMediaPlayer進行接口擴展,對上層MediaPlayer類無任何影響。
小結
適配器模式使用:
優點: 1、可以讓任何兩個沒有關聯的類一起運行。 2、提高了類的複用。 3、增加了類的透明度。 4、靈活性好。
缺點: 1、過多地使用適配器,會讓系統非常零亂,不易整體進行把握。比如,明明看到調用的是 A 接口,其實內部被適配成了 B 接口的實現,一個系統如果太多出現這種情況,無異於一場災難。因此如果不是很有必要,可以不使用適配器,而是直接對系統進行重構。 2.由於 JAVA 至多繼承一個類,所以至多隻能適配一個適配者類,而且目標類必須是抽象類。
場景:有動機地修改一個正常運行的系統的接口,這時應該考慮使用適配器模式。
注意:適配器不是在詳細設計時添加的,而是解決正在服役的項目的問題。