在软件开发中,我们经常会遇到这样的场景:新系统需要与遗留系统对接,第三方库的接口与项目现有架构不匹配,或者不同模块间的通信协议不一致。这时候,适配器模式(Adapter Pattern)就像一位专业的翻译官,让说不同"语言"的组件能够顺畅沟通。
我曾在一次企业级应用整合项目中,需要将使用SOAP协议的旧系统与采用RESTful风格的新系统对接。直接修改任何一方的代码都会带来巨大风险和工作量。通过引入适配器层,我们仅用两周就完成了原本预估需要两个月的集成工作,这就是适配器模式的威力。
适配器模式主要包含三个角色:
在Java中,我们通常有两种实现方式:
java复制class ClassAdapter extends Adaptee implements Target {
// 实现Target接口方法
public void request() {
specificRequest(); // 调用父类方法
}
}
java复制class ObjectAdapter implements Target {
private Adaptee adaptee;
public ObjectAdapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
public void request() {
adaptee.specificRequest();
}
}
提示:对象适配器更灵活,可以适配多个不同的适配者,也更符合组合优于继承的原则。
在大型项目中,不同模块可能使用不同的日志框架(Log4j、SLF4J、JUL等)。通过适配器模式,我们可以统一日志接口:
java复制// 统一日志接口
interface Logger {
void info(String message);
void error(String message);
}
// Log4j适配器
class Log4jAdapter implements Logger {
private Log4jLogger log4j;
public void info(String message) {
log4j.logInfo(message);
}
public void error(String message) {
log4j.logError(message);
}
}
电商平台需要对接多个支付渠道(支付宝、微信、银联),每个渠道的SDK接口各异:
java复制interface PaymentService {
boolean pay(BigDecimal amount);
}
class AlipayAdapter implements PaymentService {
private AlipayClient alipay;
public boolean pay(BigDecimal amount) {
return alipay.sendPayment(amount.doubleValue());
}
}
有时我们需要在两个不兼容的接口间建立双向适配:
java复制class TwoWayAdapter implements NewSystemInterface, LegacySystemInterface {
private NewSystem newSystem;
private LegacySystem legacySystem;
// 实现NewSystemInterface方法
public void newMethod() {
legacySystem.legacyOperation();
}
// 实现LegacySystemInterface方法
public void legacyOperation() {
newSystem.newMethod();
}
}
初学者常混淆这两种模式,它们的核心区别在于:
例如:
java复制// 适配器:改变InputStream接口为Reader接口
InputStreamReader adapter = new InputStreamReader(inputStream);
// 装饰器:为InputStream添加缓冲功能
BufferedInputStream decorator = new BufferedInputStream(inputStream);
频繁创建适配器可能影响性能,可以考虑使用对象池或缓存:
java复制class AdapterCache {
private static Map<Adaptee, Target> cache = new WeakHashMap<>();
public static Target getAdapter(Adaptee adaptee) {
return cache.computeIfAbsent(adaptee, ObjectAdapter::new);
}
}
利用Java动态代理实现运行时适配:
java复制class DynamicAdapter implements InvocationHandler {
private Object adaptee;
public static Target create(Object adaptee) {
return (Target) Proxy.newProxyInstance(
adaptee.getClass().getClassLoader(),
new Class[]{Target.class},
new DynamicAdapter(adaptee));
}
public Object invoke(Object proxy, Method method, Object[] args) {
// 动态调用适配者的对应方法
}
}
当目标接口方法与适配者方法不完全对应时:
java复制class SmartAdapter implements Target {
public void complexMethod(Param param) {
// 将param转换为适配者需要的多个参数
adaptee.method1(param.getA());
adaptee.method2(param.getB());
}
}
避免A适配B的同时B又适配A的情况,这会导致:
解决方案:
Spring框架大量使用适配器模式,最典型的例子是HandlerAdapter:
java复制// Spring MVC处理不同类型的控制器
public interface HandlerAdapter {
boolean supports(Object handler);
ModelAndView handle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception;
}
// 处理@Controller注解的适配器
class AnnotationMethodHandlerAdapter implements HandlerAdapter {
public boolean supports(Object handler) {
return handler instanceof HandlerMethod;
}
public ModelAndView handle(...) {
// 调用注解处理器方法
}
}
这种设计使得Spring可以同时支持多种控制器类型(如基于注解的Controller、传统的Controller接口实现等),而DispatcherServlet只需要与HandlerAdapter接口交互。
在微服务架构中,适配器模式常用于:
示例:Feign客户端的适配器实现
java复制@FeignClient(name = "legacy-service",
url = "${legacy.service.url}",
configuration = LegacyServiceConfig.class)
interface LegacyServiceAdapter {
@PostMapping("/v1/order")
Order createOrder(OrderRequest request);
}
// 配置类中定义如何适配老系统接口
class LegacyServiceConfig {
@Bean
public Encoder legacyEncoder() {
return new LegacyFormEncoder(); // 适配老系统的表单格式
}
}
为适配器编写测试时要注意:
JUnit测试示例:
java复制class AdapterTest {
@Test
void shouldAdaptLegacyToNewInterface() {
LegacyService legacy = new LegacyService();
NewService adapter = new LegacyServiceAdapter(legacy);
NewResponse response = adapter.newMethod(new NewRequest("test"));
assertEquals("TEST", response.getResult()); // 验证转换逻辑
}
}
随着技术发展,适配器模式也有一些演进形式:
java复制Function<LegacyRequest, NewRequest> adapter =
req -> new NewRequest(req.getId().toString());
当遇到以下情况时,可能需要考虑替代方案:
替代方案包括:
适配器模式体现了以下设计原则:
与其他结构型模式的比较:
在十多年的开发生涯中,我发现适配器模式最宝贵的价值在于它让系统具备了"包容性"——能够优雅地整合各种历史包袱和第三方组件。但也要警惕过度使用,有时候直接统一接口才是更根本的解决方案。特别是在微服务架构中,我建议在服务边界处就做好接口设计,减少后期适配的需要。