【设计模式】【结构型模式】适配器模式(Adpter)

👋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 adapters = new HashMap<>();

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 将字节流转换为字符流。