23种设计模式之适配器模式
目录
- 1. 简介
- 1.1 定义
- 1.2 结构和组成部分
- 2. 代码
- 2.1 MediaPlayer
- 2.2 AdvanceMediaPlayer
- 2.3 VicPlayer
- 2.4 Mp4Player
- 2.5 MediaPlayerAdapter
- 2.6 AudioPlayer
- 2.7 Test
- 3. 适用场景
- 4. 优点和缺点
- 5. 总结
1. 简介
1.1 定义
适配器模式(Adapter Pattern
)是一种结构型设计模式。它的主要作用是将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的类可以一起工作。就好像在不同国家的电源插头和插座之间需要一个转换插头(适配器)才能让电器正常使用一样。
1.2 结构和组成部分
- 目标接口(Target)
这是客户所期望的接口。客户端代码通过调用目标接口的方法来完成业务逻辑。例如,在一个图形绘制系统中,目标接口可能是IShape接口,它定义了**draw()**方法,客户端代码希望通过调用这个方法来绘制各种形状。 - 被适配者(Adaptee)
这是已经存在的、具有不兼容接口的类。它包含了一些有用的行为,但它的接口不符合客户端的期望。比如,有一个旧的图形库,它有一个OldShape类,这个类有一个display()方法,但是客户端希望调用的是**draw()**方法,这就产生了接口不兼容的情况。 - 适配器(Adapter)
适配器类实现了目标接口,并持有一个被适配者的实例。它在实现目标接口的方法时,会调用被适配者的方法来完成实际的功能。在上述图形绘制的例子中,ShapeAdapter类可以实现IShape接口,并且在draw()方法中调用OldShape类的 display() 方法,从而使得旧的图形库可以在新的系统中被使用。
2. 代码
2.1 MediaPlayer
public interface MediaPlayer {
public void play (String audioType, String fileName);
}
2.2 AdvanceMediaPlayer
public interface AdvanceMediaPlayer {
public void playVlc(String fileName);
public void playMp4(String fileName);
}
2.3 VicPlayer
public class VicPlayer implements AdvanceMediaPlayer{
@Override
public void playVlc(String fileName) {
System.out.println("Playing VLC file. Name: " + fileName);
}
@Override
public void playMp4(String fileName) {
System.out.println("Cannot play mp4 file. Name: " + fileName);
}
}
2.4 Mp4Player
public class Mp4Player implements AdvanceMediaPlayer{
public void playMp4(String fileName)
{
System.out.println("Playing mp4 file. Name: "+ fileName);
}
public void playVlc(String fileName)
{
//do nothing
}
}
2.5 MediaPlayerAdapter
public class MediaPlayerAdapter implements MediaPlayer{
private AdvanceMediaPlayer advanceMediaPlayer;
public MediaPlayerAdapter(String audioType){
if (audioType.equalsIgnoreCase("vlc")){
advanceMediaPlayer = new VicPlayer();
}else if(audioType.equalsIgnoreCase("mp4")) {
advanceMediaPlayer = new Mp4Player();
}
}
@Override
public void play(String audioType, String fileName) {
if(audioType.equalsIgnoreCase("vlc")){
advanceMediaPlayer.playVlc(fileName);
}else if(audioType.equalsIgnoreCase("mp4")){
advanceMediaPlayer.playMp4(fileName);
}
}
}
2.6 AudioPlayer
public class AudioPlayer implements MediaPlayer{
private MediaPlayerAdapter mediaPlayerAdapter;
@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")){
mediaPlayerAdapter = new MediaPlayerAdapter(audioType);
mediaPlayerAdapter.play(audioType, fileName);
}
else {
System.out.println("Invalid media. " + audioType + " format not supported");
}
}
}
2.7 Test
public class Test {
public static void main(String[] args) {
AudioPlayer audioPlayer = new AudioPlayer();
audioPlayer.play("mp3", "beyond the horizon.mp3");
audioPlayer.play("vlc", "alone.vlc");
audioPlayer.play("mp4", "alone.mp4");
}
}
运行结果:
Playing mp3 file. Name: beyond the horizon.mp3
Playing VLC file. Name: alone.vlc
Playing mp4 file. Name: alone.mp4
3. 适用场景
- 系统重构时接口不兼容的情况
当对系统进行重构,旧的组件接口和新的系统接口不兼容时,可以使用适配器模式来使得旧组件能够在新系统中继续使用。例如,一个公司更新了其软件系统的接口,但是旧的数据库访问层的接口与之不匹配,通过创建适配器,可以让旧的数据库访问层继续为新系统服务。 - 集成第三方库时接口不一致
在集成第三方库时,第三方库的接口可能与项目中的接口要求不一致。比如,项目中使用的是一种特定格式的日期接口,而第三方库返回的日期格式不同,通过适配器模式可以将第三方库的日期格式转换为项目所需要的格式。
4. 优点和缺点
- 优点
- 提高了代码的复用性:可以复用现有的不兼容类,而不需要修改它们的代码。例如,在软件系统升级过程中,很多旧的代码模块可以通过适配器继续使用,避免了重复开发。
- 增强了系统的灵活性和可扩展性:可以方便地添加新的适配器来适应新的接口变化。如果有新的不兼容的接口出现,只需要创建一个新的适配器类就可以让系统支持它。
- 缺点
- 过多的使用会使系统变得复杂:如果有大量的接口需要适配,会增加系统的复杂性,使得代码的理解和维护变得困难。因为每个适配器都需要了解目标接口和被适配者接口的细节,过多的适配器可能会导致代码的混乱。
- 可能会降低系统性能:在适配器中进行接口转换可能会带来一定的性能开销,特别是在频繁调用适配方法的情况下。不过这种性能损耗在大多数情况下是可以接受的,除非对性能要求非常高的系统。
5. 总结
其实就是实现一个插排可以插入多个不同类型充电器的功能,其中使用了一些标识进行区别,来查看需要输出什么样子的数据,调用什么接口。