适配器模式
需求
欧洲插头协议与实现
创建 EuropePlugInterface.java 接口定义欧洲插头的插口协议
/**
* @author BNTang
*/
public interface EuropePlugInterface {
/**
* 欧洲插头连接
*/
void europeConnect();
}
然后在紧接着创建 EuropePlug.java 实现欧洲插头的协议
/**
* @author BNTang
*/
public class EuropePlug implements EuropePlugInterface {
@Override
public void europeConnect() {
System.out.println("欧洲插头连接");
}
}
美州插头协议与实现
创建 UsaPlugInterface.java
/**
* @author BNTang
*/
public interface UsaPlugInterface {
/**
* 美洲插头连接
*/
void usaConnect();
}
创建 UsaPlug.java
/**
* @author BNTang
*/
public class UsaPlug implements UsaPlugInterface {
@Override
public void usaConnect() {
System.out.println("美洲插头充电");
}
}
欧洲的插座
创建 EuropeSocket.java
/**
* 欧洲的插座
*
* @author BNTang
* @date 2021/08/02
*/
public class EuropeSocket {
/**
* 连接
*
* @param europePlugInterface 欧洲插头接口
*/
void connect(EuropePlugInterface europePlugInterface) {
europePlugInterface.europeConnect();
}
}
客户端连接
创建 Client.java
/**
* @author BNTang
*/
public class Client {
public static void main(String[] args) {
EuropeSocket europeSocket = new EuropeSocket();
europeSocket.connect(new EuropePlug());
// 报错
// europeSocket.connect(new UsaPlug());
}
}
适配器模式
类适配器模式
创建 PlugAdapter.java
/**
* @author BNTang
*/
public class PlugAdapter extends UsaPlug implements EuropePlugInterface {
@Override
public void europeConnect() {
usaConnect();
}
}
修改 Client.java
/**
* @author BNTang
*/
public class Client {
public static void main(String[] args) {
EuropeSocket europeSocket = new EuropeSocket();
PlugAdapter plugAdapter = new PlugAdapter();
europeSocket.connect(plugAdapter);
}
}
对象适配器模式
修改 PlugAdapter.java
/**
* @author BNTang
*/
public class PlugAdapter implements EuropePlugInterface {
private final UsaPlug usaPlug;
public PlugAdapter(UsaPlug usaPlug) {
this.usaPlug = usaPlug;
}
@Override
public void europeConnect() {
usaPlug.usaConnect();
}
}
创建 Client.java
/**
* @author BNTang
*/
public class Client {
public static void main(String[] args) {
EuropeSocket europeSocket = new EuropeSocket();
PlugAdapter plugAdapter = new PlugAdapter(new UsaPlug());
europeSocket.connect(plugAdapter);
}
}
定义
- 将一个类的接口转换成客户希望的另外一个接口
- 使得原本由于接口不兼容而不能一起工作的那些类能一起工作
主要角色
目标(Target)接口
当前系统业务所期待的接口,它可以是抽象类或接口,EuropePlugInterface
适配者(Adaptee)类
它是被访问和适配的现存组件库中的组件接口,USAPlug
适配器(Adapter)类
它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者,PlugAdapter
UML
优缺点
优点
- 允许两个或多个不兼容的对象进行交互和通信
- 复用了现存的类,程序员不需要修改原有代码而重用现有的适配者类
- 将目标类和适配者类解耦,解决了目标类和适配者类接口不一致的问题
- 在很多业务场景中符合开闭原则
缺点
- 适配器编写过程需要结合业务场景全面考虑,可能会增加系统的复杂性
- 增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱
适用场景
- 有动机地修改一个正常运行的系统的接口,这时应该考虑使用适配器模式
- 系统需要复用现有类,而该类的接口不符合系统的需求,可以使用适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作
- 多个组件功能类似,但接口不统一且可能会经常切换时,可使用适配器模式,使得客户端可以以统一的接口使用它们
在源码中的应用
日志框架 slf4j
- 日志框架
slf4j
晚于一些主流日志框架- 它定义了日志接口
org.slf4j.Logger
,其中包含了日志记录的api
slf4j
为了能适配其他日志框架,提供了各种适配库- 比如为了适配
log4j
,在slf4j-log4j.jar
包中提供org.slf4j.impl.Log4jLoggerAdapter
记录日志的适配类Log4jLoggerAdapter
继承自org.slf4j.spi.LocationAwareLogger
继承自org.slf4j.Logger
Log4jLoggerAdapter
持有了org.apache.log4j.Logger
对象,完成了用该log4j
的日志对象实现slf4j
的日志接口中方法的适配
在 SpringMVC 中的应用
在 SpringMVC 的主控制器中,SpringMVC 中处理控制器方法
mappedHandler.getHandler()
得到的是 Controller
对象,此处并非采用直接调用 .handlerRequest
或者 MultiActionController
中编写的自定义方法,采用了一个 HandlerAdapter
的接口,为什么要使用适配器呢?此处采用了适配器模式, 由于 Controller
的类型不同,有多重实现方式,那么调用方式就不是确定的,如果需要直接调用 Controller
方法。
假设如果我们增加一个 HardController
,就要在代码中加入一行 if(mappedHandler.getHandler() instanceof HardController)
这种形式就使得程序难以维护,Spring 定义了一个适配接口,使得每一种 Controller 有一种对应的适配器实现类,让适配器代替 controller 执行相应的方法。这样在扩展 Controller 时,只需要增加一个适配器类就完成了 SpringMVC 的扩展了。每一种 Controller 创建对象控制器。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
2020-08-02 MongoDB