网站Logo Ilren 小记

Java设计模式-适配器模式

jack
24
2023-05-08

适配器模式(Adapter Pattern):兼容转换的艺术

模式简介
适配器模式是一种结构型设计模式,它允许不兼容的接口之间能够协同工作,就像电源插头转换器让不同标准的插头能在同一插座上使用。
该模式通过包装对象的方式,将被适配者的接口转换成目标接口。

📦 应用场景

  • 旧系统接口改造(遗留系统适配)

  • 第三方库/组件集成

  • 统一多个类的不一致接口

  • 数据格式转换(如JSON ↔ XML)

  • SLF4J日志门面适配各种日志框架

  • Spring MVC的HandlerAdapter

🧠 核心实现思路

  1. Target(目标接口):客户端期望的接口

  2. Adaptee(被适配者):需要被适配的现存接口

  3. Adapter(适配器):实现目标接口并包装被适配者

🧱 适配器模式的三种实现方式

1. 类适配器(继承方式)

// 目标接口(新接口)
interface ModernPrinter {
    void printDocument(String content);
}

// 被适配者(旧类)
class LegacyPrinter {
    public void print(String text, int copies) {
        for (int i = 0; i < copies; i++) {
            System.out.println("Legacy Printing: " + text);
        }
    }
}

// 适配器(继承被适配者)
class PrinterAdapter extends LegacyPrinter implements ModernPrinter {
    @Override
    public void printDocument(String content) {
        // 适配逻辑:固定copies=1
        super.print(content, 1);
    }
}

// 使用
ModernPrinter printer = new PrinterAdapter();
printer.printDocument("Hello Adapter");

2. 对象适配器(组合方式 - 推荐)

// 适配器(组合被适配者)
class PrinterAdapter implements ModernPrinter {
    private final LegacyPrinter legacyPrinter;
    
    public PrinterAdapter(LegacyPrinter printer) {
        this.legacyPrinter = printer;
    }
    
    @Override
    public void printDocument(String content) {
        legacyPrinter.print(content, 1);
    }
}

// 使用
ModernPrinter printer = new PrinterAdapter(new LegacyPrinter());

3. 接口适配器(缺省适配 - 抽象类)

// 复杂接口
interface ComplexService {
    void save();
    void update();
    void delete();
    void query();
}

// 缺省适配器(空实现)
abstract class ServiceAdapter implements ComplexService {
    public void save() {}
    public void update() {}
    public void delete() {}
    public void query() {}
}

// 按需实现部分方法
class CustomService extends ServiceAdapter {
    @Override
    public void save() {
        System.out.println("仅实现save方法");
    }
}

💎 最佳实践推荐

Spring中的HandlerAdapter

// 模拟Spring MVC的适配器
interface HandlerAdapter {
    boolean supports(Object handler);
    ModelAndView handle(HttpRequest request, Object handler);
}

// Controller适配器
class AnnotationHandlerAdapter implements HandlerAdapter {
    public boolean supports(Object handler) {
        return handler instanceof MyController;
    }
    
    public ModelAndView handle(HttpRequest request, Object handler) {
        MyController controller = (MyController)handler;
        return controller.process(request);
    }
}

// 使用适配器调度
public class DispatcherServlet {
    private List<HandlerAdapter> adapters;
    
    public ModelAndView dispatch(HttpRequest request, Object handler) {
        for (HandlerAdapter adapter : adapters) {
            if (adapter.supports(handler)) {
                return adapter.handle(request, handler);
            }
        }
        throw new RuntimeException("No adapter found");
    }
}

函数式适配(Java8+)

// 旧式接口
interface OldService {
    String execute(int code);
}

// 新式函数接口
@FunctionalInterface
interface NewService {
    Optional<String> process(String input);
}

// 适配器工厂
class ServiceAdapters {
    public static NewService adapt(OldService old) {
        return input -> {
            try {
                int code = Integer.parseInt(input);
                return Optional.ofNullable(old.execute(code));
            } catch (Exception e) {
                return Optional.empty();
            }
        };
    }
}

// 使用
OldService old = code -> "Result-" + code;
NewService adapted = ServiceAdapters.adapt(old);
adapted.process("123").ifPresent(System.out::println);

💣 常见问题与解决方案

问题1:适配器过多导致混乱?

解决方案

  • 使用自动适配器发现机制(如Spring的HandlerAdapter

  • 建立清晰的适配器命名规范(XxxToYxxAdapter

问题2:需要双向适配?

解决方案

  • 实现双向接口

class TwoWayAdapter implements NewService, OldService {
    // 实现两个接口的方法...
}

问题3:性能开销?

解决方案

  • 缓存适配器实例

  • 避免深层嵌套适配(如适配器套适配器)

📊 模式对比

实现方式

优点

缺点

适用场景

类适配器

实现简单

需要继承(Java单继承受限)

适配目标明确的情况

对象适配器

更灵活(推荐)

需要额外维护对象引用

大多数场景

接口适配器

减少不必要的实现

可能掩盖接口复杂度

部分方法适配

📚 实际应用案例

  1. 日志门面适配

    // SLF4J适配Log4j
    Logger logger = LoggerFactory.getLogger(MyClass.class);
    // 实际绑定org.slf4j.impl.Log4jLoggerAdapter
  2. JDBC驱动适配

    Connection conn = DriverManager.getConnection(url);
    // 不同数据库有各自的驱动适配器
  3. Java集合转换

    String[] arr = {"a", "b"};
    List<String> list = Arrays.asList(arr); // 数组转List的适配器

🎯 总结建议

  1. 优先选择:对象适配器(组合优于继承)

  2. 接口设计:尽量保持目标接口简洁

  3. 避免过度:不要用适配器掩盖设计缺陷

  4. 性能注意:高频调用场景考虑直接改造接口

  5. 命名规范:明确体现适配方向(如LegacyToModernAdapter

动物装饰