适配器模式:让不兼容的接口握手言和

说实话,适配器模式是我在实际项目中用得最多的设计模式之一。为什么?因为现实世界里的系统集成,从来都不是理想化的。你写了一个完美的接口,对方团队写了一个完美的接口,但两个接口就是配不上——这时候,适配器就是那个救火队员。

适配器模式的核心思想很简单:将一个类的接口转换成客户期望的另一个接口。说白了,就是给不兼容的双方搭一座桥。

生活中的适配器

你想想看,我们平时用的电源适配器,不就是这个道理吗?墙上的插座是220V交流电,你的手机需要5V直流电。没有适配器,手机就充不了电。适配器模式在软件里干的事,本质上是一样的。

我当年接手过一个遗留系统改造项目。老系统用XML传数据,新系统只认JSON。改老系统?成本太高。改新系统?不可能。最后我写了一个适配器,把XML转成JSON,两边都不动,问题就解决了。

两种实现方式

适配器模式有两种实现方式:类适配器对象适配器。我个人更推荐对象适配器,原因后面会讲。

类适配器(基于继承)

类适配器通过多重继承来实现。适配器同时继承目标接口和需要适配的类。

// 目标接口
public interface ITarget {
    void request();
}

// 需要适配的类
public class Adaptee {
    public void specificRequest() {
        System.out.println("Adaptee: 执行特殊请求");
    }
}

// 类适配器
public class ClassAdapter extends Adaptee implements ITarget {
    @Override
    public void request() {
        // 调用父类的方法
        specificRequest();
    }
}

嗯,这里要注意:Java不支持多重继承,所以类适配器在Java里只能用单继承+接口实现。C++倒是可以,但用得也不多。

对象适配器(基于组合)

对象适配器通过组合来实现。适配器持有被适配者的引用,而不是继承它。

// 对象适配器
public class ObjectAdapter implements ITarget {
    private Adaptee adaptee;
    
    public ObjectAdapter(Adaptee adaptee) {
        this.adaptee = adaptee;
    }
    
    @Override
    public void request() {
        // 委托给被适配者
        adaptee.specificRequest();
    }
}

// 使用示例
public class Client {
    public static void main(String[] args) {
        Adaptee adaptee = new Adaptee();
        ITarget target = new ObjectAdapter(adaptee);
        target.request();
    }
}

核心区别:类适配器用继承,对象适配器用组合。组合优于继承,这是我一直坚持的原则。

为什么我推荐对象适配器?

我在项目中踩过类适配器的坑。有一次,我需要适配一个第三方库的类,那个类有几十个方法。如果用类适配器,所有方法都会被继承到适配器里,造成接口污染。而且,如果被适配的类有final方法,继承这条路就堵死了。

对象适配器就没有这些问题:

  • 灵活性高:可以适配任意子类,只要传入不同的实例就行
  • 低耦合:适配器只依赖接口,不依赖具体实现
  • 可测试:被适配者可以轻松被mock

我的习惯:除非你确定被适配的类永远不会变,而且你完全控制它的源码,否则优先选对象适配器。

适配器模式的结构图

下面这张图展示了适配器模式的核心结构。我特意把两种方式都画出来了,方便对比。

适配器模式结构图 ITarget + request() Adaptee + specificRequest() ClassAdapter extends Adaptee implements ITarget ObjectAdapter - adaptee: Adaptee implements ITarget Client 实现 继承 实现 组合 使用 实现/继承 组合

实际应用场景

适配器模式在真实项目中随处可见。我列几个典型的:

场景 说明 我的经验
第三方库集成 第三方库的接口和你的系统不匹配 曾经用适配器封装了三个不同的支付SDK,对外暴露统一接口
遗留系统改造 老系统的接口不能改,新系统需要对接 做过一个数据迁移工具,适配器把老数据库的查询结果转成新格式
统一多个相似接口 多个类功能相似但接口不同 日志框架的适配,log4j、logback、slf4j之间的转换

避坑指南:我曾经犯过一个错误——在适配器里加了业务逻辑。适配器只做接口转换,不要在里面做数据校验、格式转换之外的任何事。否则适配器会变成一个大杂烩,维护成本飙升。

什么时候不该用适配器?

适配器不是万能的。我见过有人滥用适配器,把系统搞得一团糟。以下几种情况,我建议你三思:

  • 接口差异太大:如果两个接口的语义完全不同,适配器也救不了。比如一个接口是"保存订单",另一个是"发送邮件",这怎么适配?
  • 可以重构的时候:如果你能修改被适配的类,直接改接口比加适配器更干净
  • 性能敏感场景:适配器增加了一层调用,虽然开销很小,但在高频调用场景下还是要注意

总结

适配器模式,说白了就是给不兼容的接口当翻译。它不改变现有代码,只做转换。我个人觉得,这是设计模式里性价比最高的一个——简单、实用、不容易出错。

记住两个要点:优先用对象适配器不要在适配器里加业务逻辑。做到这两点,适配器模式就是你手里一把趁手的工具。

公众号:蓝海资料掘金营,微信deep3321