适配器模式详解+对象透传治理

一、什么是适配器模式?

适配器模式(Adapter Pattern)是一种结构型模式,它将不兼容的接口转换为可兼容的接口,让原本由于接口不兼容而不能一起工作的类可以一起工作。

适配器模式用于解决:

  • 封装有缺陷的接口设计
  • 统一多个类的接口设计
  • 替换依赖的外部系统
  • 兼容老版本接口

二、适配器代码示例

适配器模式两种实现方式:

  • 类适配器: 基于继承关系来实现;
  • 对象适配器: 基于组合关系来实现。

ITarget表示要转化成的接口定义。Adaptee是一组不兼容ITarget接口定义的接口,Adaptor将Adaptee转化成一组符合ITarget接口定义的接口。

2.1 类适配器

Java 复制代码
// 遗留日志组件(被适配者 Adaptee)
public class LegacyLogger {

    public static final int LEVEL_INFO = 1;
    public static final int LEVEL_WARN = 2;
    public static final int LEVEL_ERROR = 3;

    public void writeLog(int levelCode, String content) {
        System.out.println("[LegacyLogger] level=" + levelCode + " | " + content);
    }

    public void writeError(int levelCode, String content, String stackTrace) {
        System.out.println("[LegacyLogger] level=" + levelCode + " | " + content
                + " | stackTrace=" + stackTrace);
    }
}

// 统一日志门面接口(目标接口)
public interface LogTarget {

    void info(String message);

    void warn(String message);

    void error(String message, Throwable throwable);
}
 
// 类适配器(基于继承)
public class LegacyLoggerClassAdapter extends LegacyLogger implements LogTarget {

    @Override
    public void info(String message) {
        writeLog(LEVEL_INFO, message);
    }

    @Override
    public void warn(String message) {
        writeLog(LEVEL_WARN, message);
    }

    @Override
    public void error(String message, Throwable throwable) {
        writeError(LEVEL_ERROR, message, toStackTraceString(throwable));
    }
}

2.2 对象适配器

Java 复制代码
// 遗留日志组件(被适配者 Adaptee)
public class LegacyLogger {

    public static final int LEVEL_INFO = 1;
    public static final int LEVEL_WARN = 2;
    public static final int LEVEL_ERROR = 3;

    public void writeLog(int levelCode, String content) {
        System.out.println("[LegacyLogger] level=" + levelCode + " | " + content);
    }

    public void writeError(int levelCode, String content, String stackTrace) {
        System.out.println("[LegacyLogger] level=" + levelCode + " | " + content
                + " | stackTrace=" + stackTrace);
    }
}

// 统一日志门面接口(目标接口)
public interface LogTarget {

    void info(String message);

    void warn(String message);

    void error(String message, Throwable throwable);
}

// 对象适配器(基于组合)
public class LegacyLoggerObjectAdapter implements LogTarget {

    private final LegacyLogger legacyLogger;

    public LegacyLoggerObjectAdapter(LegacyLogger legacyLogger) {
        this.legacyLogger = legacyLogger;
    }

    @Override
    public void info(String message) {
        legacyLogger.writeLog(LegacyLogger.LEVEL_INFO, message);
    }

    @Override
    public void warn(String message) {
        legacyLogger.writeLog(LegacyLogger.LEVEL_WARN, message);
    }

    @Override
    public void error(String message, Throwable throwable) {
        legacyLogger.writeError(LegacyLogger.LEVEL_ERROR, message, toStackTraceString(throwable));
    }
}

三、适配器实现方式选择

  1. 如果Adaptee接口并不多,那两种实现方式都可以。
  2. 如果Adaptee接口很多,而且Adaptee和ITarget接口定义大部分都相同,推荐使用类适配器,因为Adaptor复用父类Adaptee的接口,代码量要少一些。
  3. 如果Adaptee接口很多,而且Adaptee和ITarget接口定义大部分都不相同,推荐使用对象适配器,因为组合结构相对于继承更加灵活。

四、对象透传治理

理想情况下,防腐层(Anti-Corruption Layer, ACL)应当采用适配器模式来实现。其核心思想是:在调用外部 RPC 接口后,立即通过适配器将返回的外部对象转换为本服务内部的领域对象,从而完成对外部模型的隔离与防腐,避免了外部数据结构、语义或变更对本服务造成污染与耦合。

然而,理想很丰满,现实很骨感,项目中的大量老代码(俗称"屎山代码")可能并未遵循防腐层的设计原则,而是直接使用外部 RPC 接口返回的对象,并让其在本服务内部全链路流转。作为肩负治理职责的"铲屎官",我们不得不直面这类代码。对此,可以采用"同名类覆盖"的方式进行治理,具体步骤如下:

  1. 在本项目中构建与外部返回对象全类名完全一致的同名类,即包路径与类名均保持完全相同;
  2. 在该同名类中仅保留本服务实际所需的字段,剔除冗余与无关属性,以实现对外部模型的精简与收敛;
  3. 在线下环境进行编译部署,借助 Java 类加载机制------本项目 classpath 下的同名类会优先于外部依赖中的类被加载------来验证该同名类能否正确生效、项目能否正常运行;
  4. 验证无误后,对该同名类进行位置迁移:不再保持与外部对象相同的包路径,而是将其移动至防腐层所对应的正确包位置下;
  5. 通过 set 方法将外部对象逐字段转换为内部对象,实现代码结构的规范化与内外模型的彻底解耦。
相关推荐
怕浪猫1 小时前
3 小时搭一个能用的聊天机器人后端,我用了这套技术栈
后端·面试·github
xiaoqiMikko2 小时前
Redis 实战(二):宕机会丢多少数据 —— kill -9 与真断电下 RDB / AOF 逐个实测(6.2.4)
redis·后端
李哈哈8522 小时前
微服务依赖冲突
后端
努力的小雨2 小时前
每天盯着屏幕写代码,我终于不想再凑合了
后端
海岳云舟2 小时前
springboot项目中使用AOP
后端
小稀土1232 小时前
5步跑通第一个迭代:敏捷研发管理平台落地最小闭环
后端
zz在掘金337082 小时前
Django 中间件全家桶背诵笔记
后端
斑鸠喳喳2 小时前
调度线程池执行器 ScheduledThreadPoolExecutor
后端
大哥43092 小时前
索引 mapping 与代码对不上:一次不报错的静默失效排查
后端