👋hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD
🔥 2025本人正在沉淀中... 博客更新速度++
👍 欢迎点赞、收藏、关注,跟上我的更新节奏
🎵 当你的天空突然下了大雨,那是我在为你炸乌云
一、入门
什么是适配器模式?
适配器模式是Java中常用的结构型设计模式,它的核心作用就像现实中的电源转换器一样---让原本不兼容的两个接口能够协同工作。
为什么要用适配器模式?
假设我们需要在电商系统中接入两种第三方支付:
支付宝支付:方法名为 aliPay(String orderId, BigDecimal amount)
微信支付:方法名为 wechatPay(int merchantId, String orderNo, double money)
痛点:两个支付接口方法名、参数类型、参数顺序完全不同,导致业务代码中支付逻辑混乱。
// 支付宝SDK(参数:订单ID + 金额)
class AliPaySDK {
public boolean aliPay(String orderId, BigDecimal amount) {
System.out.println("支付宝支付成功:" + orderId + " 金额:" + amount);
return true;
}
}
// 微信支付SDK(参数:商户ID + 订单号 + 金额)
class WechatPaySDK {
public boolean wechatPay(int merchantId, String orderNo, double money) {
System.out.println("微信支付成功:商户" + merchantId + " 订单:" + orderNo + " 金额:" + money);
return true;
}
}
如何实现适配器模式?
适配器模式(Adapter)包含以下主要角色:
目标(Target)接口:当前系统业务所期待的接口,它可以是抽象类或接口。
适配者(Adaptee)类:它是被访问和适配的现存组件库中的组件接口。
适配器(Adapter)类:它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。
【案例】三方支付 - 改
目标接口:UnifiedPayment类。当前系统业务所期待的接口,它可以是抽象类或接口。
interface UnifiedPayment {
/**
* 统一支付方法
* @param unifiedOrder 包含商户ID、订单号、金额等
*/
boolean pay(UnifiedOrder unifiedOrder);
}
// 统一订单参数对象
class UnifiedOrder {
private int merchantId;
private String orderId;
private BigDecimal amount;
// 构造方法、getters省略
}
适配器:AliPayAdapter类和WechatPayAdapter类适配者:AliPaySDK类和WechatPaySDK类
// 支付宝适配器
class AliPayAdapter implements UnifiedPayment {
private AliPaySDK aliPay = new AliPaySDK();
@Override
public boolean pay(UnifiedOrder order) {
// 参数转换与适配逻辑
return aliPay.aliPay(
order.getOrderId(), // 直接使用统一订单号
order.getAmount() // 金额类型匹配
);
}
}
// 微信支付适配器
class WechatPayAdapter implements UnifiedPayment {
private WechatPaySDK wechatPay = new WechatPaySDK();
@Override
public boolean pay(UnifiedOrder order) {
// 微信特殊处理逻辑
String wechatOrderNo = "WX_" + order.getOrderId();
return wechatPay.wechatPay(
order.getMerchantId(), // 从统一参数获取商户ID
wechatOrderNo, // 转换订单号格式
order.getAmount().doubleValue() // BigDecimal转double
);
}
}
改造后业务层
public class PaymentService {
private Map
public PaymentService() {
adapters.put(PayType.ALI_PAY, new AliPayAdapter());
adapters.put(PayType.WECHAT_PAY, new WechatPayAdapter());
}
public boolean pay(PayType type, UnifiedOrder order) {
return adapters.get(type).pay(order); // 统一调用
}
}
⚠注意:适配器这层不做业务逻辑校验
适配器这层不做业务逻辑校验,如下所示
class WechatPayAdapter implements UnifiedPayment {
// 错误!适配器不应包含业务判断
public boolean pay(UnifiedOrder order) {
if (order.amount > 10000) {
// 业务规则不应在这里
throw new Exception("超额限制");
}
// ...转换逻辑
}
}
二、适配器模式在框架源码中的运用
Java I/O 的 InputStreamReader
InputStreamReader将字节流(InputStream)转换为字符流(Reader),解决字节流与字符流接口不兼容问题。
目标接口:Reader
适配器:InputStreamReader
适配者:InputStream
public class InputStreamReader extends Reader {
private final StreamDecoder sd; // 关键适配器
public InputStreamReader(InputStream in) {
this(in, Charset.defaultCharset());
}
// 通过StreamDecoder适配字节→字符
public int read() throws IOException {
return sd.read();
}
}
Spring MVC 的 HandlerAdapter
Spring 要支持多种 Controller 类型(如 @Controller、HttpRequestHandler、Servlet 等),但不同 Controller的请求处理方式不同。目标接口:HandlerAdapter接口
public interface HandlerAdapter {
boolean supports(Object handler);
ModelAndView handle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception;
}
适配器:RequestMappingHandlerAdapter类和SimpleServletHandlerAdapter类。
// 具体适配器示例:处理注解式Controller
public class RequestMappingHandlerAdapter implements HandlerAdapter {
public boolean supports(Object handler) {
return (handler instanceof HandlerMethod);
}
public ModelAndView handle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
// 实际调用被注解的Controller方法
HandlerMethod handlerMethod = (HandlerMethod) handler;
return invokeHandlerMethod(request, response, handlerMethod);
}
}
// 另一个适配器:处理Servlet
public class SimpleServletHandlerAdapter implements HandlerAdapter {
public boolean supports(Object handler) {
return (handler instanceof Servlet);
}
public ModelAndView handle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
((Servlet) handler).service(request, response);
return null;
}
}
适配者:我们用户实现的接口。
// 注解驱动的 Controller
@Controller
public class MyController {
@RequestMapping("/hello")
public String hello() {
return "hello";
}
}
// 基于接口的 Controller:
public class MyHttpRequestHandler implements HttpRequestHandler {
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response) {
// 处理请求
}
}
// 传统的 Servlet
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
// 处理请求
}
}
三、总结
适配器模式的优点
解耦性:
将客户端与具体实现分离,客户端只依赖目标接口,无需关心底层实现。
例如:Spring MVC 的 DispatcherServlet 只依赖 HandlerAdapter 接口,不关心具体 Controller 的实现。
复用性:
可以复用现有的类或第三方库,而无需修改其源码。
例如:通过 InputStreamReader 复用 InputStream 的功能。
扩展性:
新增适配器即可支持新的实现,符合开闭原则。
例如:Spring MVC 新增一种 Controller 类型时,只需添加对应的 HandlerAdapter。
透明性:○ 客户端无需感知适配器的存在,调用方式与普通接口一致。○ 例如:使用 SLF4J 日志框架时,无需关心底层是Logback还是Log4j。
适配器模式的缺点
增加复杂性:
引入适配器会增加类的数量,可能导致系统复杂度上升。
例如:一个系统中有多种数据格式转换时,可能需要多个适配器。
性能开销:
适配器通常需要额外的逻辑(如数据转换),可能带来一定的性能损耗。
例如:InputStreamReader 在字节到字符的转换过程中会有解码开销。
过度使用问题:
如果滥用适配器模式,可能导致系统层次过多,难以维护。
例如:在简单的场景中,直接修改接口可能比引入适配器更合适。
适配器模式的适用场景
整合遗留代码
场景:系统中存在老旧的模块或第三方库,无法直接修改其源码。
示例:将老式的 XML 数据接口适配为新的 JSON 接口。
统一接口规范
场景:需要将多个不同的实现统一为相同的接口。
示例:Spring MVC 的 HandlerAdapter 统一处理多种 Controller 类型。
兼容不同版本
场景:系统升级时,需要同时支持新旧版本的接口。
示例:API 升级时,通过适配器兼容旧版客户端。
跨平台或跨系统集成
场景:需要与外部系统对接,但接口规范不一致。
示例:将微信支付的接口适配为统一的支付接口。
数据格式转换
场景:需要将一种数据格式转换为另一种格式。
示例:InputStreamReader 将字节流转换为字符流。