适配器模式
问题
什么是适配器模式?Java 和 Spring 中有哪些典型应用?
答案
核心思想
将一个类的接口转换成客户端期望的另一个接口,使原本不兼容的类可以一起工作。就像电源适配器把 220V 转成 5V。
类适配器 vs 对象适配器
| 方式 | 实现 | 优缺点 |
|---|---|---|
| 类适配器 | 继承 Adaptee + 实现 Target | Java 单继承限制 |
| 对象适配器 | 持有 Adaptee 引用(组合) | 更灵活,推荐 |
实现示例
适配器模式 - 统一日志接口
// 目标接口:项目统一的日志接口
public interface Logger {
void info(String msg);
void error(String msg, Throwable t);
}
// 被适配者:第三方 SDK 的日志接口,方法签名不一样
public class ThirdPartyLogger {
public void log(int level, String message, Exception ex) { /* ... */ }
}
// 适配器:将第三方日志接口转换为项目统一接口
public class ThirdPartyLoggerAdapter implements Logger {
private final ThirdPartyLogger adaptee;
public ThirdPartyLoggerAdapter(ThirdPartyLogger adaptee) {
this.adaptee = adaptee;
}
@Override
public void info(String msg) {
adaptee.log(1, msg, null); // level 1 = INFO
}
@Override
public void error(String msg, Throwable t) {
adaptee.log(3, msg, (Exception) t); // level 3 = ERROR
}
}
Java/Spring 中的典型应用
| 应用 | 说明 |
|---|---|
Arrays.asList() | 数组适配为 List 接口 |
InputStreamReader | 字节流适配为字符流 |
Collections.enumeration() | Iterator 适配为 Enumeration |
| SLF4J | 统一日志门面,适配 Log4j/Logback 等 |
Spring MVC HandlerAdapter | 不同类型的 Controller 适配为统一处理流程 |
Spring MVC HandlerAdapter
// Spring MVC 的 DispatcherServlet 需要统一调用 handle()
// 但 Controller 有多种形式:@Controller、HttpRequestHandler、Servlet...
// HandlerAdapter 负责将不同类型适配为统一接口
public interface HandlerAdapter {
boolean supports(Object handler);
ModelAndView handle(HttpServletRequest req, HttpServletResponse resp, Object handler);
}
// RequestMappingHandlerAdapter:处理 @RequestMapping 注解的方法
// HttpRequestHandlerAdapter:处理 HttpRequestHandler 接口
// SimpleControllerHandlerAdapter:处理 Controller 接口
常见面试问题
Q1: 适配器模式和装饰器模式的区别?
答案:
| 维度 | 适配器模式 | 装饰器模式 |
|---|---|---|
| 目的 | 接口转换,解决不兼容问题 | 增强功能,添加新行为 |
| 接口 | 输入和输出接口不同 | 装饰前后接口相同 |
| 使用时机 | 对接已有代码/第三方库 | 需要动态增加功能 |
Q2: SLF4J 的适配器设计?
答案:
SLF4J 定义了统一的日志接口(Logger),通过不同的桥接包适配不同的日志实现:
slf4j-log4j12:SLF4J → Log4jlogback-classic:SLF4J → Logbackslf4j-jdk14:SLF4J → JUL
项目代码只面向 SLF4J 接口编程,切换日志实现只需换桥接包,不改业务代码。