适配器模式属于结构型模式,主要解决两个已存在的功能相近的接口间不能兼容的问题。在准备将一个接口对接到另一个接口中时,发现另一个接口(目标)与准备对接的接口(适配者)因不兼容而无法正常运行功能时,就可以使用适配器模式来增加一个适配器对接二者。不要说二者谁是主谁是客,只有适配者通过适配器适配目标。当我想把一个功能模块加到另一个模块中时,要加的这个模块就是适配者。
文章目录
- 适配器模式的介绍
- 优点
- 缺点
- 应用场景
- 适配器的使用
- 使用对象适配器方式
- 类图
- 原有的架构
- 使用适配器接入视频模块
- 第一步,创建视频媒体接口
- 第二步,创建MP4播放器和WMV播放器
- 第三步,创建视频适配器
- 第四步,编写测试类测试
- 使用类适配器方式
- 类图
- 原有的架构
- 使用适配器接入视频模块
- 第一步,创建MP4适配器
- 第二步,创建WMV适配器
- 第三步,在测试类中测试
适配器模式的介绍
适配器模式通过实现目标接口,同时继承或关联适配者接口来完成功能的适配,让两个不相关的接口能够共同工作。
优点
- 灵活性高,符合开闭原则
- 提高了类的复用性(本来因为接口不兼容而无法使用之前的功能,只能重新添加功能,但现在可以通过适配器调用之前的功能了)
缺点
- 过多使用适配器会导致项目的结构混乱
应用场景
-
适配器其实是在现有设计存在缺陷时作为一种“补偿模式”使用,如果设计之初能够规避接口不兼容的问题,那就没必要使用适配器。但人与人之间存在差异,那不兼容问题就无可避免,比如各国的电压不同,插座的设计也不同,这就必须使用适配器。
-
Java与数据库连接的驱动工具JDBC也是一个适配器,Java提供一个抽象接口,由不同的数据库厂商在数据库引擎接口与JDBC接口间实现一个适配器(JDBC驱动),以此达到Java能够连接不同数据库的功能。
-
当需要对接第三方提供给我的接口时,发现该接口的定义与我实现的功能接口存在差异时需要使用适配器模式。
-
当开发新系统时,发现之前系统的功能模块可以复用但又于当前系统接口不兼容时可以使用适配器模式。
适配器的使用
当前系统存在一个媒体播放器,但是编写播放器时只考虑到了播放音频,所以只存在一个MP3播放器和音频媒体的接口。但现在我想让播放器可以播放视频,并已经编写了一组视频媒体的播放器。可准备对接媒体播放器时发现它只支持音频媒体,于是我决定使用适配器解决兼容问题。
适配器有两种实现方式,分别为类适配器和对象适配器。类适配器使用的是继承关系来实现,对象适配使用的是关联关系来实现。
使用对象适配器方式
对象适配器实现了目标接口(音频媒体),同时内部持有适配者接口(视频媒体)的实例,然后再按照目标的接口的方法去转换适配者实现类。
类图
原有的架构
音频媒体
package 设计模式.结构型模式.适配器模式;
public interface 音频媒体 {
String 播放音乐(String 名称);
}
MP3播放器
package 设计模式.结构型模式.适配器模式;
public class MP3播放器 implements 音频媒体 {
@Override
public String 播放音乐(String 名称) {
return "准备播放MP3格式的音乐:" + 名称;
}
}
媒体播放器
package 设计模式.结构型模式.适配器模式;
public class 媒体播放器 {
public void 播放(音频媒体 音乐, String 名称){
System.out.println(音乐.播放音乐(名称));
}
}
测试类
package 设计模式.结构型模式.适配器模式;
public class 测试类 {
public static void main(String[] args) {
媒体播放器 播放器 = new 媒体播放器();
MP3播放器 mp3 = new MP3播放器();
播放器.播放(mp3, "小苹果.mp3");
}
}
测试效果
使用适配器接入视频模块
以下的步骤都是在音频播放器原有的架构上搭建,不会影响到之前的类。
第一步,创建视频媒体接口
视频媒体
package 设计模式.结构型模式.适配器模式;
public interface 视频媒体 {
String 播放视频(String 名称);
}
第二步,创建MP4播放器和WMV播放器
MP4播放器
package 设计模式.结构型模式.适配器模式;
public class MP4播放器 implements 视频媒体 {
@Override
public String 播放视频(String 名称) {
return "现在开始播放MP4格式的视频:"+名称;
}
}
WMV播放器
package 设计模式.结构型模式.适配器模式;
public class WMV播放器 implements 视频媒体 {
@Override
public String 播放视频(String 名称) {
return "现在开始播放WMV格式的视频:"+名称;
}
}
第三步,创建视频适配器
视频适配器
package 设计模式.结构型模式.适配器模式;
public class 视频适配器 implements 音频媒体 {
视频媒体 视频;
视频适配器(视频媒体 视频){
this.视频 = 视频;
}
@Override
public String 播放音乐(String 名称) {
// 实现音频媒体的接口,与视频媒体进行对接
return 视频.播放视频(名称);
}
}
第四步,编写测试类测试
测试类
package 设计模式.结构型模式.适配器模式;
public class 测试类 {
public static void main(String[] args) {
媒体播放器 播放器 = new 媒体播放器();
MP3播放器 mp3 = new MP3播放器();
播放器.播放(mp3, "小苹果.mp3");
视频适配器 适配器 = new 视频适配器(new MP4播放器());
播放器.播放(适配器, "勇敢的心.mp4");
播放器.播放(new 视频适配器(new WMV播放器()), "疯狂动物城.wmv");
}
}
测试结果
使用类适配器方式
使用类适配器时,适配器实现目标接口方法,同时继承适配者实现类,而我创建了两个视频播放器,那就需要创建两个适配器来转换。
类图
原有的架构
和对象适配器中的原有架构相同
使用适配器接入视频模块
以下的步骤都是在音频播放器原有的架构上搭建,并且省略了视频媒体接口和媒体播放器的创建,因为结构与上方的一样。
第一步,创建MP4适配器
MP4适配器
package 设计模式.结构型模式.适配器模式;
public class MP4适配器 extends MP4播放器 implements 音频媒体 {
@Override
public String 播放音乐(String 名称) {
return 播放视频(名称);
}
}
第二步,创建WMV适配器
WMV适配器
package 设计模式.结构型模式.适配器模式;
public class WMV适配器 extends WMV播放器 implements 音频媒体 {
@Override
public String 播放音乐(String 名称) {
return 播放视频(名称);
}
}
第三步,在测试类中测试
测试类
package 设计模式.结构型模式.适配器模式;
public class 测试类 {
public static void main(String[] args) {
媒体播放器 播放器 = new 媒体播放器();
MP3播放器 mp3 = new MP3播放器();
播放器.播放(mp3, "小苹果.mp3");
播放器.播放(new MP4适配器(), "勇敢的心.mp4");
播放器.播放(new WMV适配器(), "疯狂动物城.wmv");
}
}
测试结果