在软件系统迭代过程中,最让人头疼的问题莫过于接口不兼容。上周我就遇到一个典型案例:旧系统使用XML格式数据交互,而新采购的第三方服务只提供JSON接口。如果直接修改旧系统,至少需要改动30多个模块,测试周期长达两周。这时候,适配器模式就成了救命稻草。
适配器模式(Adapter Pattern)本质上是一种"接口转换器",就像电源插头转换器能让美标插头在国内插座上使用一样。它通过包装不兼容接口,在不修改原有代码的前提下实现协同工作。这种设计完美遵循了开闭原则——对扩展开放,对修改关闭。
类适配器采用继承机制,典型结构如下:
java复制// 目标接口(客户端期望的接口)
interface Target {
void request();
}
// 需要适配的类
class Adaptee {
public void specificRequest() {
System.out.println("特殊请求处理");
}
}
// 适配器类
class ClassAdapter extends Adaptee implements Target {
@Override
public void request() {
specificRequest(); // 转换调用
}
}
这种方式的优势在于:
但存在明显局限:
实战建议:当需要适配的类方法较少,且确定不会同时适配多个类时,这种方案最简洁高效。
对象适配器采用组合方式,结构如下:
java复制class ObjectAdapter implements Target {
private Adaptee adaptee;
public ObjectAdapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
adaptee.specificRequest();
}
}
相比类适配器,对象适配器:
代价是需要额外维护被适配对象的引用。在Spring等DI框架中,这种模式尤为常见。
最近在金融项目中遇到典型场景:风控系统需要接收Map格式数据,而征信系统返回的是Protobuf对象。我们设计了这样的适配器:
java复制public class RiskControlAdapter {
public static Map<String, Object> adapt(CreditReport protoData) {
Map<String, Object> result = new HashMap<>();
result.put("creditScore", protoData.getScore());
result.put("overdueRecords",
protoData.getRecordsList().stream()
.map(r -> Map.of("date", r.getDate(), "amount", r.getAmount()))
.collect(Collectors.toList()));
// 其他复杂字段转换...
return result;
}
}
关键处理技巧:
当同步接口需要适配异步服务时,可以采用回调机制:
java复制public class AsyncAdapter implements SyncService {
private final AsyncService asyncService;
public AsyncAdapter(AsyncService asyncService) {
this.asyncService = asyncService;
}
@Override
public Result syncOperation(Param param) {
CompletableFuture<Result> future = new CompletableFuture<>();
asyncService.asyncOperation(param,
result -> future.complete(result),
error -> future.completeExceptionally(error));
try {
return future.get(5, TimeUnit.SECONDS);
} catch (Exception e) {
throw new ServiceException("Adapter timeout", e);
}
}
}
注意事项:
在高频调用场景下,适配器实例的频繁创建会成为性能瓶颈。我们通过对象池优化:
java复制public class AdapterPool {
private static final Pool<ObjectAdapter> pool = new GenericObjectPool<>(
new BasePooledObjectFactory<>() {
@Override
public ObjectAdapter create() {
return new ObjectAdapter(new Adaptee());
}
// 其他重写方法...
}
);
public static Target getAdapter() {
return pool.borrowObject();
}
public static void returnAdapter(Target adapter) {
if (adapter instanceof ObjectAdapter) {
pool.returnObject((ObjectAdapter)adapter);
}
}
}
实测QPS从1200提升到8500,但要注意:
当适配器与被适配对象相互持有时,可能导致内存泄漏:
java复制// 错误示例
class LeakAdapter implements Target {
private Adaptee adaptee;
public LeakAdapter(Adaptee adaptee) {
this.adaptee = adaptee;
adaptee.setAdapter(this); // 双向引用!
}
//...
}
解决方案:
在微服务改造过程中,我们组合使用适配器和外观模式:
plantuml复制@startuml
class LegacySystem {
+methodA()
+methodB()
}
class NewService1 {
+operationX()
}
class NewService2 {
+operationY()
}
class Facade {
-adapter1: Adapter
-adapter2: Adapter
+unifiedInterface()
}
class Adapter {
+adapt()
}
LegacySystem --> Facade
NewService1 --> Adapter
NewService2 --> Adapter
Adapter --> Facade
@enduml
这种架构实现了:
对于需要支持多种协议的SDK,我们采用策略模式+适配器:
java复制public interface ProtocolAdapter {
Request adapt(Request input);
}
public class JsonAdapter implements ProtocolAdapter {
// 实现JSON转换
}
public class XmlAdapter implements ProtocolAdapter {
// 实现XML转换
}
public class ProtocolRouter {
private Map<ProtocolType, ProtocolAdapter> adapters;
public Request route(ProtocolType type, Request input) {
return adapters.get(type).adapt(input);
}
}
扩展新协议时只需新增适配器实现,符合OCP原则。