【西瓜带你学设计模式 | 第七期 - 适配器模式】适配器模式 —— 类适配器与对象适配器实现、优缺点与适用场景

文章目录

    • 前言
    • [1. 适配器模式是什么?](#1. 适配器模式是什么?)
    • [2. 为什么要用适配器?能解决什么问题?](#2. 为什么要用适配器?能解决什么问题?)
    • [3. 实现思路](#3. 实现思路)
    • [4. 类适配器 vs 对象适配器](#4. 类适配器 vs 对象适配器)
    • [5. 示例一:把"老接口"适配成"新接口"(对象适配器)](#5. 示例一:把“老接口”适配成“新接口”(对象适配器))
      • [5.1 目标接口(客户端期望)](#5.1 目标接口(客户端期望))
      • [5.2 已有类(第三方/老系统:接口不一致)](#5.2 已有类(第三方/老系统:接口不一致))
      • [5.3 适配器(把 String 转成 byte[])](#5.3 适配器(把 String 转成 byte[]))
      • [5.4 客户端使用](#5.4 客户端使用)
    • [6. 示例二:常见"返回值适配"(支付/日志/响应统一)](#6. 示例二:常见“返回值适配”(支付/日志/响应统一))
      • [6.1 目标响应](#6.1 目标响应)
      • [6.2 已有类返回老格式](#6.2 已有类返回老格式)
      • [6.3 适配器把 OldPayResult 转成 ApiResponse](#6.3 适配器把 OldPayResult 转成 ApiResponse)
    • [7. 适配器模式常见结构](#7. 适配器模式常见结构)
    • [8. 优缺点](#8. 优缺点)
    • [9. 适用场景](#9. 适用场景)
    • [10. 总结](#10. 总结)

前言

在很多项目里,会遇到这种情况:已经存在一个类/第三方库/老系统接口 ,但它的"输入输出/方法命名/数据结构"跟当前系统想要的不一致。

不可能为了兼容它就把原来的代码大改一遍------于是就需要一个"翻译器"把不匹配的接口对齐。

这就是**适配器模式(Adapter Pattern)**要解决的问题。


1. 适配器模式是什么?

适配器模式:把一个类的接口"包装/转换"成客户端期望的接口,让原本因为接口不兼容而无法一起工作的类能协同工作。

它常见的角色可以理解为:

  • 目标接口(Target):客户端期望调用的方法集合
  • 已有类(Adaptee):手上已经有的、但接口不匹配的类(第三方/老系统)
  • 适配器(Adapter):实现目标接口,并内部调用已有类,把数据/行为进行转换

2. 为什么要用适配器?能解决什么问题?

适配器模式通常在以下场景非常常见:

  1. 第三方接口不符合系统规范
    • 比如第三方库返回 Result<T>,但系统统一用 ApiResponse<T>
  2. 老代码接口需要被新系统"继续使用"
    • 不想重写老系统逻辑,只做外部适配
  3. 同一功能需要对不同来源做兼容
    • 例如支付渠道:微信/支付宝接口不同,但上层都希望统一 pay() 形式

3. 实现思路

适配器模式的"落地流程"一般是:

  1. 定义系统期望的接口(Target)
  2. 找到已有类并分析它与目标接口的差异(参数类型、返回值、单位、字段含义等)
  3. 写一个适配器类(Adapter):
    • 对外实现 Target
    • 内部持有 Adaptee(组合为主)
    • 把参数/返回结果转换成目标格式

4. 类适配器 vs 对象适配器

GoF 里常见两种结构(思想类似):

  • 类适配器(继承方式):Adapter 继承已有类 Adaptee,同时实现 Target
  • 对象适配器(组合方式):Adapter 内部持有 Adaptee 实例,并实现 Target

更推荐"对象适配器",因为更符合组合优于继承,也更灵活。

下面用"对象适配器"展开讲解。


5. 示例一:把"老接口"适配成"新接口"(对象适配器)

5.1 目标接口(客户端期望)

比如客户端希望所有打印机都实现统一接口:

java 复制代码
public interface Printer {
    void print(String text);
}

5.2 已有类(第三方/老系统:接口不一致)

java 复制代码
public class OldPrinter {
    // 老系统要求打印的是字节数组
    public void printBytes(byte[] bytes) {
        System.out.println("OldPrinter printing: " + new String(bytes));
    }
}

5.3 适配器(把 String 转成 byte[])

java 复制代码
public class PrinterAdapter implements Printer {
    private final OldPrinter oldPrinter;

    public PrinterAdapter(OldPrinter oldPrinter) {
        this.oldPrinter = oldPrinter;
    }

    @Override
    public void print(String text) {
        byte[] bytes = text.getBytes();          // 参数转换
        oldPrinter.printBytes(bytes);           // 调用已有类
    }
}

5.4 客户端使用

java 复制代码
public class Demo {
    public static void main(String[] args) {
        OldPrinter old = new OldPrinter();

        Printer printer = new PrinterAdapter(old); // 关键:对外统一成 Printer
        printer.print("Hello Adapter!");
    }
}

效果 :客户端完全不需要关心 OldPrinter 怎么打印,只能调用 Printer.print()


6. 示例二:常见"返回值适配"(支付/日志/响应统一)

很多适配器不是只做参数转换,还会做返回值/数据结构转换

6.1 目标响应

java 复制代码
public class ApiResponse<T> {
    public int code;
    public String msg;
    public T data;

    public ApiResponse(int code, String msg, T data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }
}

6.2 已有类返回老格式

java 复制代码
public class OldPayResult {
    public boolean ok;
    public String reason;
    public String payId;
}

6.3 适配器把 OldPayResult 转成 ApiResponse

java 复制代码
public class PayAdapter {
    private final OldPayService oldPayService;

    public PayAdapter(OldPayService oldPayService) {
        this.oldPayService = oldPayService;
    }

    public ApiResponse<String> pay(String orderId) {
        OldPayResult r = oldPayService.pay(orderId);

        if (r.ok) {
            return new ApiResponse<>(0, "success", r.payId);
        }
        return new ApiResponse<>(-1, r.reason, null);
    }
}

这类适配器的价值在于:让业务层只认识统一协议,而不需要理解第三方细节。


7. 适配器模式常见结构

可以把 Adapter 的工作理解为两件事:

  1. 接口对齐:Adapter 对外暴露客户端需要的接口
  2. 语义/格式转换:Adapter 负责字段、单位、编码、异常模型等差异

8. 优缺点

优点

  • 让原本不兼容的类可以协作(兼容第三方/老系统)
  • 符合开闭原则:不需要修改已有类,只新增适配器
  • 业务层更干净:客户端只依赖统一接口

缺点

  • 类会变多:每个差异都可能带来一个 Adapter
  • 适配器可能成为"翻译逻辑的集中地",需要避免过度膨胀
  • 语义转换如果做得不好,可能隐藏真实差异导致"看似能用但行为不一致"

9. 适用场景

适配器模式适用于:

  • 要复用第三方库/老系统,但接口不匹配
  • 希望把多个来源包装成统一接口
  • 不想改动 Adaptee(或不允许改动)

不太适用于:

  • 差异非常大且改动频繁,适配器会越来越复杂(可能需要重构/替换方案)
  • "适配器"实际上在做大量业务逻辑,应该考虑拆分或调整架构

10. 总结

当发现"已有能力能用,但接口不符合系统期待",就应该考虑适配器模式:用一个 Adapter 做翻译,让系统其余部分保持稳定。

相关推荐
前端付豪2 小时前
实现消息级操作栏
前端·人工智能·后端
Oliver_LaVine2 小时前
idea启动后端项目-控制台中文乱码处理
java·ide
Flittly2 小时前
【SpringAIAlibaba新手村系列】(6)PromptTemplate 提示词模板与变量替换
java·spring boot·agent
yaaakaaang2 小时前
3.springboot,用eclipse轻松创建~
java·spring boot·eclipse
计算机学姐2 小时前
基于SpringBoot的新能源充电桩管理系统
java·vue.js·spring boot·后端·mysql·spring·java-ee
木井巳2 小时前
【笔试强训】Day1
java·算法
风萧萧19992 小时前
Milvus Java 快速入门
java·开发语言·milvus
leiming62 小时前
巧用 FreeRTOS 任务通知作“邮箱”:NeoPixel 灯环控制实战
java·前端·算法
东离与糖宝2 小时前
Java 26 FFM API进阶:零JNI调用TensorRT/OpenVINO,AI端到端延迟砍半
java·人工智能