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

👋hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD
🔥 2025本人正在沉淀中… 博客更新速度++
👍 欢迎点赞、收藏、关注,跟上我的更新节奏
🎵 当你的天空突然下了大雨,那是我在为你炸乌云

一、入门

什么是适配器模式?

适配器模式是Java中常用的结构型设计模式,它的核心作用就像现实中的电源转换器一样—让原本不兼容的两个接口能够协同工作。

为什么要用适配器模式?

假设我们需要在电商系统中接入两种第三方支付:

  1. 支付宝支付:方法名为 aliPay(String orderId, BigDecimal amount)
  2. 微信支付:方法名为 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<PayType, UnifiedPayment> 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 类型(如 @ControllerHttpRequestHandlerServlet 等),但不同 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) {
        // 处理请求
    }
}

三、总结

适配器模式的优点

  1. 解耦性
    • 将客户端与具体实现分离,客户端只依赖目标接口,无需关心底层实现。
    • 例如:Spring MVC 的 DispatcherServlet 只依赖 HandlerAdapter 接口,不关心具体 Controller 的实现。
  2. 复用性
    • 可以复用现有的类或第三方库,而无需修改其源码。
    • 例如:通过 InputStreamReader 复用 InputStream 的功能。
  3. 扩展性
    • 新增适配器即可支持新的实现,符合开闭原则。
    • 例如:Spring MVC 新增一种 Controller 类型时,只需添加对应的 HandlerAdapter
  4. 透明性
    ○ 客户端无需感知适配器的存在,调用方式与普通接口一致。
    ○ 例如:使用 SLF4J 日志框架时,无需关心底层是Logback还是Log4j

适配器模式的缺点

  1. 增加复杂性
    • 引入适配器会增加类的数量,可能导致系统复杂度上升。
    • 例如:一个系统中有多种数据格式转换时,可能需要多个适配器。
  2. 性能开销
    • 适配器通常需要额外的逻辑(如数据转换),可能带来一定的性能损耗。
    • 例如:InputStreamReader 在字节到字符的转换过程中会有解码开销。
  3. 过度使用问题
    • 如果滥用适配器模式,可能导致系统层次过多,难以维护。
    • 例如:在简单的场景中,直接修改接口可能比引入适配器更合适。

适配器模式的适用场景

  1. 整合遗留代码
    • 场景:系统中存在老旧的模块或第三方库,无法直接修改其源码。
    • 示例:将老式的 XML 数据接口适配为新的 JSON 接口。
  2. 统一接口规范
    • 场景:需要将多个不同的实现统一为相同的接口。
    • 示例:Spring MVC 的 HandlerAdapter 统一处理多种 Controller 类型。
  3. 兼容不同版本
    • 场景:系统升级时,需要同时支持新旧版本的接口。
    • 示例:API 升级时,通过适配器兼容旧版客户端。
  4. 跨平台或跨系统集成
    • 场景:需要与外部系统对接,但接口规范不一致。
    • 示例:将微信支付的接口适配为统一的支付接口。
  5. 数据格式转换
    • 场景:需要将一种数据格式转换为另一种格式。
    • 示例:InputStreamReader 将字节流转换为字符流。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值