适配器模式
适配器模式是一种结构型设计模式,其主要作用是解决两个不兼容接口之间的兼容性问题。适配器模式通过引入一个适配器来将一个类的接口转换成客户端所期望的另一个接口,从而让原本由于接口不匹配而无法协同工作的类能够协同工作。
结构
适配器模式(Adapter)包含以下主要角色:
- 目标(Target)接口:当前系统业务所期待的接口,它可以是抽象类或接口。
- 适配者(Adaptee)类:它是被访问和适配的现存组件库中的组件接口。
- 适配器(Adapter)类:它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。
图例:
AudioPlayer实现了 MediaPlayer 接口,只可以播放 mp3 。实现了 AdvancedMediaPlayer 接口的类则可以播放 vlc 和 mp4 格式的文件。可以创建一个实现了 MediaPlayer 接口的适配器类 MediaAdapter,并使用 AdvancedMediaPlayer 的实现类对象来播放所需的格式。AdapterPatternDemo 类则可以使用 AudioPlayer 类来播放各种格式的音频。
对象适配器模式代码案例:
// 目标接口
interface MediaPlayer {
void play(String audioType, String filename);
}
// 适配器接口
interface AdvancedMediaPlayer {
void playVlc(String filename);
void playMp4(String filename);
}
// 适配器类
class MediaAdapter implements MediaPlayer {
private AdvancedMediaPlayer advancedMediaPlayer;
public MediaAdapter(String audioType) {
if (audioType.equalsIgnoreCase("vlc")) {
advancedMediaPlayer = new VlcPlayer();
} else if (audioType.equalsIgnoreCase("mp4")) {
advancedMediaPlayer = new Mp4Player();
}
}
@Override
public void play(String audioType, String filename) {
if (audioType.equalsIgnoreCase("vlc")) {
advancedMediaPlayer.playVlc(filename);
} else if (audioType.equalsIgnoreCase("mp4")) {
advancedMediaPlayer.playMp4(filename);
}
}
}
// 具体实现类
class AudioPlayer implements MediaPlayer {
MediaAdapter mediaAdapter;
@Override
public void play(String audioType, String filename) {
if (audioType.equalsIgnoreCase("mp3")) {
System.out.println("Playing mp3 file. Name: " + filename);
} 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");
}
}
}
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
}
}
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);
}
}
// 使用示例
public class Main {
public static void main(String[] args) {
AudioPlayer audioPlayer = new AudioPlayer();
audioPlayer.play("mp3", "song.mp3");
audioPlayer.play("vlc", "movie.vlc");
audioPlayer.play("mp4", "video.mp4");
}
}
适配器模式有类适配器模式和对象适配器模式;这里使用对象适配器模式主要是类适配器模式违背了合成复用原则,它限制了适配器类只能适配一个具体的被适配者类。且Java 不支持多重继承,因此在 Java 中一般使用接口来实现类似的功能
比如下面类适配器,采用的是继承:
// 适配器类(类适配器)
class MediaAdapter extends Mp4Player implements MediaPlayer {
@Override
public void play(String audioType, String filename) {
if (audioType.equalsIgnoreCase("vlc")) {
playVlc(filename);
} else if (audioType.equalsIgnoreCase("mp4")) {
playMp4(filename);
}
}
}
当然,也有接口适配器模式,不过使用相对较少。当一个接口拥有许多方法,但实现类只需要实现其中一部分方法时,可以使用接口适配器模式,提供一个抽象适配器类实现该接口,并提供默认实现,从而避免实现类需要实现大量空方法。
使用场景:
- 当需要使用一个已经存在的类,但是它的接口不符合当前需求时,可以考虑使用适配器模式。
- 当需要复用一些已经存在的类,但是接口与其他类不兼容时,可以考虑使用适配器模式。
- 当需要创建一个可复用的类,该类可以与不相关或不可预见的类协同工作时,可以考虑使用适配器模式。