适配器模式作为一种结构型设计模式,在软件系统设计开发中使用到的频率非常之高,比如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 至多继承一个类,所以至多只能适配一个适配者类,而且目标类必须是抽象类。
场景:有动机地修改一个正常运行的系统的接口,这时应该考虑使用适配器模式。
注意:适配器不是在详细设计时添加的,而是解决正在服役的项目的问题。