八:适配器模式
1、从实现方式上分为两种,类适配器和对象适配器,这两种的区别在于实现方式上的不同,一种采用继承,一种采用组合的方式(继承和实现接口方式)。
2、从使用目的上来说,也可以分为两种,特殊适配器和缺省适配器,这两种的区别在于使用目的上的不同,一种为了复用原有的代码并适配当前的接口,一种为了提供缺省的实现,避免子类需要实现不该实现的方法。
注意:首先应该明白一点,适配器模式是补救措施,所以在系统设计过程中请忘掉这个设计模式,这个模式只是在你无可奈何时的补救方式。
那么我们什么时候使用这个模式呢 ?
场景通常情况下是,系统中有一套完整的类结构,而我们需要利用其中某一个类的功能(通俗点说可以说是方法),但是我们的客户端只认识另外一个和这个类结构不相关的接口,这时候就是适配器模式发挥的时候了,我们可以将这个现有的类与我们的目标接口进行适配,最终获得一个符合需要的接口并且包含待复用的类的功能的类。
下面先写一个特殊适配器:
package com.example.api.demo.shejimoshi.shipeiqimoshi.twoWay.test2;
/**
* 特殊适配器模式用于将一个类的接口转换成客户端所期望的另一个接口。在上面的示例中,
* 特殊适配器的作用是将Adaptee类的specificRequest方法适配到Target接口的request方法上。
*/
// 目标接口
interface Target {
void request();
}
// 需要适配的类
class Adaptee {
void specificRequest() {
System.out.println("Adaptee's specific request");
}
}
// 特殊适配器
class SpecialAdapter implements Target {
private Adaptee adaptee;
public SpecialAdapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
// 调用 Adaptee 的方法以适配 Target 接口
adaptee.specificRequest();
}
}
// 使用示例
public class Main {
public static void main(String[] args) {
Adaptee adaptee = new Adaptee();
SpecialAdapter adapter = new SpecialAdapter(adaptee);
adapter.request();
}
}
下面再写一个 缺省适配器:
package com.example.api.demo.shejimoshi.shipeiqimoshi.twoWay.test1;
/**
* 缺省适配器模式(Default Adapter Pattern)通常用于当一个接口中有多个方法,但是我们只需要实现其中的一部分方法时。
* 这种情况下,我们可以使用缺省适配器模式来提供一个默认的空实现,从而让子类有选择地覆盖它们感兴趣的方法。
*/
// 监听器接口
interface Listener {
void onEvent1();
void onEvent2();
}
// 缺省适配器
abstract class DefaultListener implements Listener {
public void onEvent1() {
System.out.println("Default implementation of onEvent1");
}
public void onEvent2() {
System.out.println("Default implementation of onEvent2");
}
}
// 子类只需要覆盖它们想要自定义的方法
class CustomListener extends DefaultListener {
public void onEvent1() {
System.out.println("Custom implementation of onEvent1");
}
}
// 使用示例
public class Main {
public static void main(String[] args) {
CustomListener listener = new CustomListener();
listener.onEvent1(); // 使用子类自定义的实现
listener.onEvent2(); // 使用缺省实现
}
}
如果还有测试别的适配情况,欢迎留言!
补充一个新的场景,有一个新的需求,要能够播放视频文件。但是我们发现现有的 MediaPlayer 接口无法直接支持视频播放。为了解决这个问题,我们引入一个适配器 AdvancedMediaPlayer 来兼容新的视频播放需求。
/**
* 适配器模式(Adapter Pattern)
* 用于将一个类的接口转换成客户端所期望的另一个接口。它允许不兼容的接口之间进行协同工作
*/
public interface MediaPlayer {
//旧的:有一个已经存在的 MediaPlayer 接口,定义了播放音频文件的方法:
void playAudio(String audioType, String fileName);
}
public interface AdvancedMediaPlayer {
// 第一步: 新加一个先进的可以播放视频vcd的接口, 保留原有的功能,并且增加新的功能
// AdvancedMediaPlayer 接口的实现类要实现 <旧 + 新> 逻辑)这里只需要1个参数。
void playVideo(String fileName);
}
//第2步: 新加功能,我们实现了 AdvancedMediaPlayer。实现新的播放vcd的逻辑
class VcdPlayer implements AdvancedMediaPlayer {
@Override
public void playVideo(String fileName) {
System.out.println("Playing vcd file: " + fileName);
}
}
//第3步:
//旧的功能, 应该是直接实现 MediaPlayer 接口, 需要2個参数
//修改不要直接实现接口 MediaPlayer, 而是改成 添加实现 AdvancedMediaPlayer, 实现方法copy过来旧的 播放的逻辑)改这里只需要1个入参,
//多出来的1个参数,在适配器里面用来做type类型 = 选择播放器种类
class Mp4Player implements AdvancedMediaPlayer {
@Override
public void playVideo(String fileName) {
System.out.println("Playing MP4 file: " + fileName);
}
}
//第4步: 添加 适配器类 MediaAdapter,它实现了旧的MediaPlayer接口,(由2个入参,改造成调新的接口实现方法只需 1个入参)
//并通过组合一个 接口 AdvancedMediaPlayer 对象来实现适配
public class MediaAdapter implements MediaPlayer {
//第5步: 定义一个实例变量(MediaAdapter的一个属性)它用于持有一个实现 AdvancedMediaPlayer 接口的对象实例。
//MediaAdapter 可以调用具体的 AdvancedMediaPlayer 实现类(如 VcdPlayer 或 Mp4Player)的方法。
private AdvancedMediaPlayer advancedMediaPlayer;
//第6步: 定义构造函数,接受1个参数,它在创建 MediaAdapter 对象时被调用。
public MediaAdapter(String audioType) {
if (audioType.equalsIgnoreCase("vcd")) {
advancedMediaPlayer = new VcdPlayer();
} else if (audioType.equalsIgnoreCase("mp4")) {
advancedMediaPlayer = new Mp4Player();
}
}
//第7步: 实现原有的接口 MediaPlayer的方法,通过构造函数已经适配到了具体的播放器,
//之前这里是实现MP4Player的功能,只需要1个参数。现在改到适配器里面,具体功能放到后面实现类,实现新的接口,需要2个参数。
@Override
public void playAudio(String audioType, String fileName) {
advancedMediaPlayer.playVideo(fileName);
}
}
public class ClientTest {
/**
* 第8步: 测试
* 有一个新的需求,要能够播放视频文件。但是我们发现现有的 MediaPlayer 接口无法直接支持视频播放。
* 为了解决这个问题,我们引入一个适配器 AdvancedMediaPlayer 来兼容新的视频播放需求。
*/
public static void main(String[] args) {
//放MP4音频
MediaPlayer mediaPlayer = new MediaAdapter("mp4");
mediaPlayer.playAudio("mp4", "video.mp4");
//放vcd影片
mediaPlayer = new MediaAdapter("vcd");
mediaPlayer.playAudio("vcd", "video.vcd");
}
}
学海无涯 代码作伴
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南