跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Javajava算法

设计模式精讲:适配器模式的原理与 Java 实现

适配器模式用于解决接口不兼容问题,通过中间件连接源与目标。介绍其三种实现形式(类、对象、接口),结合订单接口适配与日志收集系统的 Java 代码示例,分析 MyBatis 日志框架中的应用,阐述如何在保留旧代码基础上扩展新功能,同时指出过度使用的潜在风险。

清心发布于 2025/1/19更新于 2026/10/883 浏览
设计模式精讲:适配器模式的原理与 Java 实现

适配器模式详解

适配器模式(Adapter Pattern)是解决项目新老版本接口兼容问题的常用手段。当新系统需要调用旧系统的接口,但两者定义不一致时,通过一个适配器作为中间件进行转换,使得原本无法直接调用的老版本接口能够在新环境中复用,同时不影响老版本已有的既定实现。这不仅提高了代码的可复用性,也增强了系统的可扩展性。

核心角色与原理

在适配器模式中,主要涉及三个角色:

  1. 源(Adaptee):需要被适配的对象或类型,通常是旧版本接口或特定功能的实现,一般不能随意改动。
  2. 适配器(Adapter):连接目标和源的中间对象,相当于插头转换器,负责将源角色的功能转换为目标角色期望的格式。
  3. 目标(Target):期待得到的目标接口,通常是新版本的标准接口,支持动态扩展。

这种模式满足开闭原则:对扩展开放,对修改关闭。具体到适配器模式中,意味着我们可以动态扩展目标角色,而无需改动源角色。

源角色和目标角色的关联关系通常是一对多的(源 : 目标 = 1:N)。根据实现方式的不同,适配器模式主要分为三种形式:

  • 类适配器模式:适配器类继承源类,并实现目标接口。
  • 对象适配器模式:适配器类实现目标接口,内部持有源类的实例(组合方式)。
  • 接口适配器模式:当不需要重写接口中的所有方法时,提供一个抽象类作为过渡,默认提供空方法实现,子类只需重写需要的方法。

典型应用场景

适配器模式的应用非常广泛,特别是在以下场景:

  1. 项目新老版本迭代:保留老版本既定功能,同时动态扩展新功能,减少老版本代码改动量。
  2. 第三方框架集成:例如 MyBatis 框架支持多种日志系统(Log4J、SLF4J 等),内部使用统一的日志接口来兼容不同类型的日志框架。

优缺点分析

优点: 系统可以使用现有的类,即使其接口不符合当前需求。通过适配器模式,可以让这些功能得到更好的复用,自然地扩展系统功能。

缺点: 过多的使用适配器会让系统变得零乱,不易整体把握。比如明明调用的是 A 接口,实际内部被适配成了 B 接口的实现。如果系统中出现大量此类情况,无异于一场灾难。因此,如果不是很有必要,建议直接对系统进行重构。

代码实战:订单接口适配

假设早期 V1 版本的订单接口入参为 Map 类型,随着业务更新,V2 版本需要支持 List 类型。在不改变原有接口代码的情况下,如何实现?

首先看原有的 Map 处理逻辑:

public void froOrderMap(Map map) {
    for (int i = 0; i < map.size(); i++) {
        // 使用索引作为 MapKey 获取数据
        String value = (String) map.get(i);
        System.out.println("value:" + value);
    }
}

我们需要创建一个适配器,让 List 看起来像 Map 一样被访问。这里采用类适配器思路,继承 HashMap 并重写关键方法:

public class ListAdapter extends HashMap {
    private List list;

    public ListAdapter(List list) {
        this.list = list;
    }

    @Override
    public int size() {
        return list.size();
    }

    @Override
    public Object get(Object key) {
        // 将 Map 的 Key 转换为 List 的索引
        return list.get(Integer.valueOf(key.toString()).intValue());
    }
}

测试运行效果如下:

public class Test {
    public static void main(String[] args) {
        // 1. 获取集合类型用户
        List member = new MemberService().getMember();
        
        // 2. 创建适配器,将 List 包装成 Map 风格
        ListAdapter listAdapter = new ListAdapter(member);
        
        // 3. 调用原有接口,适配器自动完成转换
        new OrderService().froOrderMap(listAdapter);
    }
}

实战案例:日志收集系统

设计一个日志收集系统,可能需要写入文件、MQ 或数据库。为了统一接口,我们定义一个标准的目标接口 LogWriteDbService,并通过适配器将现有的文件服务适配过去。

1. 定义基本实体与接口
public class LogBean {
    private String logId;
    private String logText;

    public String getLogId() { return logId; }
    public String getLogText() { return logText; }
    public void setLogId(String logId) { this.logId = logId; }
    public void setLogText(String logText) { this.logText = logText; }
}

public interface LogWriteFileService {
    void logWriteFile();
    List<LogBean> readLogFile();
}

public class LogWriteFileServiceImpl implements LogWriteFileService {
    @Override
    public void logWriteFile() {
        System.out.println(">>>将日志写入文件中...");
    }

    @Override
    public List<LogBean> readLogFile() {
        LogBean log1 = new LogBean();
        log1.setLogId("0001");
        log1.setLogText("Tomcat 启动成功..");
        
        LogBean log2 = new LogBean();
        log2.setLogId("0002");
        log2.setLogText("Jetty 启动成功..");
        
        List<LogBean> listArrayList = new ArrayList<>();
        listArrayList.add(log1);
        listArrayList.add(log2);
        return listArrayList;
    }
}
2. 对象适配器实现

对象适配器通过组合的方式,将源对象注入适配器中,实现目标接口。

public class LogAdapter implements LogWriteDbService {
    private LogWriteFileService logWriteFileService;

    public LogAdapter(LogWriteFileService logWriteFileService) {
        this.logWriteFileService = logWriteFileService;
    }

    @Override
    public void writeDbFile(LogBean logBean) {
        // 1. 从文件中读取日志文件
        List<LogBean> logBeans = logWriteFileService.readLogFile();
        // 2. 写入到数据库中
        logBeans.add(logBean);
        System.out.println(">>>将数据写入到数据库中..");
        // 3. 写入到本地文件中
        logWriteFileService.logWriteFile();
    }
}
3. 类适配器实现

类适配器直接继承源类,实现目标接口。这种方式在 Java 中受限于单继承特性,不如对象适配器灵活,但在某些场景下更轻量。

public class ClassAdapter extends LogWriteFileServiceImpl implements LogWriteDbService {
    @Override
    public void writeDbFile(LogBean logBean) {
        // 1. 从文件中读取日志文件
        List<LogBean> logBeans = readLogFile();
        // 2. 写入到数据库中
        logBeans.add(logBean);
        System.out.println(">>>将数据写入到数据库中..");
        // 3. 写入到本地文件中
        logWriteFile();
    }
}

MyBatis 日志收集分析

Java 开发中常用的日志框架有 Log4j、Log4j2、SLF4J 等。MyBatis 定义了一套统一的日志接口供上层使用,并为上述常用框架提供了相应的适配器。

在 MyBatis 的日志模块中,内部使用了 org.apache.ibatis.logging.Log 接口。由于第三方日志框架的对外接口各不相同,MyBatis 为了复用和集成这些组件,在其日志模块中提供了多种 Adapter,将这些第三方日志组件的接口适配成 org.apache.ibatis.logging.Log。这样 MyBatis 就可以通过统一的 Log 接口调用第三方日志了。在这里,MyBatis 内部的统一日志接口对应适配器模式中的源,而各类第三方日志系统提供的 Logger 对应目标,MyBatis 提供的各类 Adapter 则负责兼容这两者。

在实际开发中,合理使用适配器模式能有效降低耦合度,但也要警惕过度设计带来的维护成本。

目录

  1. 适配器模式详解
  2. 核心角色与原理
  3. 典型应用场景
  4. 优缺点分析
  5. 代码实战:订单接口适配
  6. 实战案例:日志收集系统
  7. 1. 定义基本实体与接口
  8. 2. 对象适配器实现
  9. 3. 类适配器实现
  10. MyBatis 日志收集分析

更多推荐文章

查看全部
  • OpenClaw 免费 AI 大模型配置方法
  • 系统架构师技术复盘:Flowable、软考备考与 AIGC 实践之路
  • DeepSeek 深度使用指南:提示词技巧与本地知识库搭建
  • 宇树机器人 G1 二次开发:FAST-LIO 建图与 RViz 配置
  • Linux OpenEuler 环境下 Milvus 在线与离线安装卸载及连接异常处理
  • 在 Cursor 中配置 MCP 服务实现自动化开发
  • 鸿蒙 Flutter 智能家居应用开发实战指南
  • DeepSeek-R1-Distill-Qwen-1.5B 部署指南:vLLM+Open WebUI 低显存运行方案
  • 智能交通系统的 FPGA 进化论:从基础信号灯到动态调优
  • 阿里开源纯前端浏览器自动化 PageAgent 技术解析
  • AI 辅助开发在线图书借阅平台设计与实现
  • Claude Code + Figma:AI 辅助原型设计实战指南
  • PID 算法基础:比例积分微分控制原理
  • Node.js 最新版下载安装与环境配置教程
  • GitLens 实战指南:VS Code 版本控制可视化入门
  • 前端安全:别让你的应用变成黑客的游乐场
  • 停车场收入统计算法题解
  • Linux 下基于 Docker 部署 Android 模拟器及远程访问方案
  • SHA512 算法详解
  • Java 泛型基础、自定义及通配符详解

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online