6、Java 适配器模式从入门到实战

张开发
2026/4/16 19:15:47 15 分钟阅读

分享文章

6、Java 适配器模式从入门到实战
Java 适配器模式从入门到实战后端必看前言适配器模式是Java结构型设计模式中最常用的模式之一核心作用是“解决接口不兼容问题”——就像生活中的电源适配器能让不同插头的设备正常通电。在后端开发中我们经常遇到“旧系统接口无法修改、第三方SDK接口不匹配、不同模块接口规范不一致”的场景此时适配器模式就能完美解决无需修改原有代码实现接口的无缝对接。很多开发者只会用但说不清楚原理面试时被问起三种实现方式的区别就卡壳本文从入门到实战结合真实业务场景可运行代码面试高频考点带你吃透适配器模式看完直接能用、能说、能面试新手也能快速上手。一、为什么需要适配器模式痛点直击先看一个Java后端开发中最常见的真实场景假设你维护一个旧电商系统其中订单模块有一个旧的订单查询接口返回XML格式现在新开发的前端页面只支持JSON格式的接口且旧接口代码不能修改可能被其他系统依赖你该如何实现对接如果没有适配器模式你可能会这么做直接修改旧接口代码让其返回JSON格式——违反“开闭原则”且可能影响其他依赖该接口的系统在前端代码中做XML转JSON的处理——增加前端复杂度不符合“后端负责数据格式转换”的开发规范在新接口中重复编写旧接口的业务逻辑再返回JSON——代码冗余后期维护成本极高。而适配器模式的核心价值就是在不修改原有接口和调用方代码的前提下通过一个“适配器”类实现两个不兼容接口的对接既保证原有系统稳定又满足新需求同时遵循开闭原则和单一职责原则。核心结论当系统中存在“接口不兼容、无法直接修改原有代码、需要对接第三方接口”的场景时优先使用适配器模式。二、适配器模式核心概念极简理解适配器模式的核心思想“定义一个适配器类实现目标接口同时持有适配者旧接口/第三方接口的引用通过适配器将适配者的接口转换为目标接口让调用方可以通过目标接口直接调用适配者的功能”。简单说适配器就是“中间转换器”一边对接旧接口适配者一边对接新接口目标屏蔽接口差异让调用方无需关心适配细节。适配器模式有3个核心角色必须掌握面试常考结合实战理解无需死记硬背Target目标接口调用方期望使用的接口定义了调用方需要的功能规范如上述场景中“返回JSON格式的订单查询接口”Adaptee适配者需要被适配的原有接口/第三方接口功能已经存在但接口规范与目标接口不兼容如上述场景中“返回XML格式的旧订单查询接口”Adapter适配器核心类实现目标接口同时持有适配者的引用将适配者的接口转换为目标接口实现两者的兼容如“XML转JSON的订单查询适配器”。重点补充Java适配器模式有三种常见实现方式分别是类适配器、对象适配器、接口适配器其中对象适配器是企业开发中最常用的耦合度最低类适配器因继承关系耦合度高接口适配器适用于接口方法过多的场景下文会逐一实战讲解。三、适配器模式三种实现方式手写实战必掌握以“旧订单接口XML适配新订单接口JSON”为统一场景分别实现三种适配器对比差异掌握各自的适用场景。场景准备定义目标接口Target和适配者Adaptee先定义两个不兼容的接口为后续适配器实现做准备代码可直接复制运行。/** * 1. 目标接口Target新系统期望的接口返回JSON格式 * 调用方前端只关注这个接口无需关心适配逻辑 */publicinterfaceOrderJsonService{// 目标方法查询订单返回JSON字符串StringqueryOrderJson(StringorderNo);}/** * 2. 适配者Adaptee需要被适配的旧接口返回XML格式 * 假设这个类是旧系统代码不能修改 */publicclassOrderXmlService{// 适配者方法查询订单返回XML字符串publicStringqueryOrderXml(StringorderNo){// 模拟旧系统业务逻辑返回XML格式数据returnorderorderNoorderNo/orderNogt;lt;amountgt;199.9lt;/amountgt;lt;/ordergt;;}}实现一类适配器继承适配者实现目标接口核心适配器类继承适配者Adaptee同时实现目标接口Target通过重写目标方法调用适配者的方法完成接口转换。/** * 类适配器继承适配者OrderXmlService实现目标接口OrderJsonService * 缺点因Java单继承特性适配器无法继承其他类耦合度较高实际开发中较少使用 */publicclassOrderClassAdapterextendsOrderXmlServiceimplementsOrderJsonService{OverridepublicStringqueryOrderJson(StringorderNo){// 1. 调用适配者旧接口的方法获取XML数据StringxmlDatasuper.queryOrderXml(orderNo);// 2. 核心将XML格式转换为JSON格式模拟转换逻辑实际开发中可用FastJSON等工具StringjsonDataxmlToJson(xmlData);// 3. 返回JSON数据满足目标接口要求returnjsonData;}// 模拟XML转JSON的工具方法实际开发中可使用第三方工具类privateStringxmlToJson(Stringxml){// 简化逻辑实际需解析XML节点StringorderNoxml.substring(xml.indexOf(orderNo)9,xml.indexOf(/orderNo));Stringamountxml.substring(xml.indexOf(amount)8,xml.indexOf(/amount));return{\orderNo\:\orderNo\,\amount\:amount};}}类适配器测试代码/** * 类适配器测试类 */publicclassClassAdapterTest{publicstaticvoidmain(String[]args){// 调用方直接使用目标接口无需关心适配细节OrderJsonServiceorderServicenewOrderClassAdapter();// 调用目标方法获取JSON格式数据StringjsonorderService.queryOrderJson(ORDER20260415001);System.out.println(类适配器返回JSONjson);}}运行结果类适配器返回JSON{orderNo:ORDER20260415001,amount:199.9}类适配器核心要点避坑优点实现简单直接继承适配者无需额外持有适配者引用缺点受Java单继承限制适配器无法继承其他类耦合度高不适用于适配者是接口的场景适用场景适配者是具体类且不需要继承其他类的简单场景。实现二对象适配器持有适配者引用实现目标接口核心适配器类不继承适配者而是持有适配者的引用通过构造方法注入同时实现目标接口通过调用适配者的方法完成接口转换。这是企业开发中最常用的方式耦合度低、扩展性强。/** * 对象适配器持有适配者引用实现目标接口推荐使用 * 优点不依赖继承耦合度低可适配多个适配者扩展性强 */publicclassOrderObjectAdapterimplementsOrderJsonService{// 持有适配者引用通过构造方法注入便于扩展和测试privateOrderXmlServiceorderXmlService;// 构造方法注入适配者实例publicOrderObjectAdapter(OrderXmlServiceorderXmlService){this.orderXmlServiceorderXmlService;}OverridepublicStringqueryOrderJson(StringorderNo){// 1. 调用适配者旧接口的方法获取XML数据StringxmlDataorderXmlService.queryOrderXml(orderNo);// 2. XML转JSON复用工具方法StringjsonDataxmlToJson(xmlData);// 3. 返回JSON数据returnjsonData;}// 复用XML转JSON工具方法privateStringxmlToJson(Stringxml){StringorderNoxml.substring(xml.indexOf(orderNo)9,xml.indexOf(/orderNo));Stringamountxml.substring(xml.indexOf(amount)8,xml.indexOf(/amount));return{\orderNo\:\orderNo\,\amount\:amount};}}对象适配器测试代码/** * 对象适配器测试类企业开发常用 */publicclassObjectAdapterTest{publicstaticvoidmain(String[]args){// 1. 创建适配者实例旧接口OrderXmlServicexmlServicenewOrderXmlService();// 2. 创建适配器注入适配者OrderJsonServiceorderServicenewOrderObjectAdapter(xmlService);// 3. 调用目标方法获取JSON数据StringjsonorderService.queryOrderJson(ORDER20260415002);System.out.println(对象适配器返回JSONjson);}}运行结果对象适配器返回JSON{orderNo:ORDER20260415002,amount:199.9}对象适配器核心要点重点掌握优点不依赖继承规避Java单继承限制通过构造方法注入适配者耦合度低可灵活替换适配者扩展性强缺点需要额外持有适配者引用代码量略多于类适配器适用场景企业开发主流场景尤其是适配者可能变化、需要适配多个适配者的情况。实现三接口适配器抽象类实现目标接口空实现所有方法核心当目标接口中方法过多而调用方只需要使用其中部分方法时定义一个抽象适配器类实现目标接口的所有方法空实现调用方只需继承抽象适配器重写自己需要的方法即可避免实现不必要的方法。/** * 目标接口方法较多调用方只需要部分方法 * 模拟一个复杂的订单接口包含多个方法 */publicinterfaceOrderService{// 方法1查询订单JSON格式StringqueryOrderJson(StringorderNo);// 方法2查询订单XML格式备用StringqueryOrderXml(StringorderNo);// 方法3取消订单booleancancelOrder(StringorderNo);// 方法4修改订单金额booleanupdateOrderAmount(StringorderNo,doubleamount);}/** * 接口适配器抽象类实现目标接口空实现所有方法 * 作用避免调用方实现不必要的方法简化开发 */publicabstractclassOrderInterfaceAdapterimplementsOrderService{// 空实现所有方法调用方按需重写OverridepublicStringqueryOrderJson(StringorderNo){returnnull;}OverridepublicStringqueryOrderXml(StringorderNo){returnnull;}OverridepublicbooleancancelOrder(StringorderNo){returnfalse;}OverridepublicbooleanupdateOrderAmount(StringorderNo,doubleamount){returnfalse;}}/** * 调用方只需要使用“查询订单JSON”和“取消订单”方法 * 继承接口适配器只重写需要的方法无需实现所有方法 */publicclassOrderClientAdapterextendsOrderInterfaceAdapter{// 持有适配者引用旧接口privateOrderXmlServiceorderXmlService;publicOrderClientAdapter(OrderXmlServiceorderXmlService){this.orderXmlServiceorderXmlService;}// 重写需要的方法1查询订单JSONOverridepublicStringqueryOrderJson(StringorderNo){StringxmlDataorderXmlService.queryOrderXml(orderNo);returnxmlToJson(xmlData);}// 重写需要的方法2取消订单OverridepublicbooleancancelOrder(StringorderNo){// 模拟取消订单逻辑System.out.println(取消订单orderNo);returntrue;}// XML转JSON工具方法privateStringxmlToJson(Stringxml){StringorderNoxml.substring(xml.indexOf(orderNo)9,xml.indexOf(/orderNo));Stringamountxml.substring(xml.indexOf(amount)8,xml.indexOf(/amount));return{\orderNo\:\orderNo\,\amount\:amount};}}接口适配器测试代码/** * 接口适配器测试类 */publicclassInterfaceAdapterTest{publicstaticvoidmain(String[]args){OrderXmlServicexmlServicenewOrderXmlService();OrderServiceorderServicenewOrderClientAdapter(xmlService);// 调用重写的方法StringjsonorderService.queryOrderJson(ORDER20260415003);booleancancelorderService.cancelOrder(ORDER20260415003);System.out.println(接口适配器返回JSONjson);System.out.println(订单取消结果cancel);// 未重写的方法调用抽象适配器的空实现返回null/falseSystem.out.println(orderService.queryOrderXml(ORDER20260415003));}}运行结果取消订单ORDER20260415003 接口适配器返回JSON{orderNo:ORDER20260415003,amount:199.9} 订单取消结果true null接口适配器核心要点优点简化开发调用方无需实现目标接口的所有方法只需重写需要的方法缺点只能适配一个目标接口灵活性不如对象适配器适用场景目标接口方法较多且调用方只需要使用其中部分方法的场景如Spring中的HandlerAdapter。三种适配器实现方式对比面试必背实现方式核心实现优点缺点适用场景类适配器继承适配者实现目标接口实现简单无需持有适配者引用受单继承限制耦合度高适配者是具体类无需继承其他类对象适配器持有适配者引用实现目标接口耦合度低扩展性强无继承限制需额外持有适配者引用代码量略多企业开发主流场景适配者可能变化接口适配器抽象类实现目标接口空实现所有方法简化开发避免实现不必要的方法只能适配一个目标接口灵活性差目标接口方法多调用方只需部分方法四、实战二适配器模式进阶结合Spring企业实战实际企业开发中我们不会手动创建适配器实例而是结合Spring框架的依赖注入DI将适配器、适配者交给Spring容器管理进一步降低耦合同时规避Spring XML配置中链接失效的问题优先使用注解配置。场景升级对接第三方支付SDK接口不兼容假设项目中需要对接第三方支付宝SDK第三方SDK的接口方法与项目内部的支付接口不兼容使用对象适配器推荐结合Spring实现适配无需修改项目原有接口和第三方SDK代码。步骤1定义项目内部目标接口Target/** * 项目内部目标接口支付接口项目统一规范 */publicinterfacePayService{// 项目内部支付方法参数为订单号、金额返回支付结果JSONStringpay(StringorderNo,doubleamount);}步骤2定义第三方SDK适配者Adaptee假设这是第三方支付宝SDK的接口无法修改接口规范与项目内部不一致。/** * 适配者第三方支付宝SDK接口无法修改 * 接口规范与项目内部PayService不兼容 */publicclassAlipaySdk{// 第三方SDK支付方法参数为订单信息JSON返回支付结果XMLpublicStringalipay(StringorderInfo){// 模拟第三方SDK支付逻辑System.out.println(第三方支付宝SDK支付orderInfo);returnresultorderNo{orderNo}/orderNostatussuccess/status/result;}}步骤3实现对象适配器结合Spring注解使用Spring的Component注解将适配器、适配者交给Spring管理通过Autowired注入适配者实现接口转换。importorg.springframework.stereotype.Component;// 适配者交给Spring管理第三方SDK实例ComponentpublicclassAlipaySdk{publicStringalipay(StringorderInfo){System.out.println(第三方支付宝SDK支付orderInfo);returnresultorderNo{orderNo}/orderNostatussuccess/status/result;}}// 适配器交给Spring管理实现目标接口ComponentpublicclassAlipayAdapterimplementsPayService{// Autowired注入适配者第三方SDK实例无需手动newAutowiredprivateAlipaySdkalipaySdk;OverridepublicStringpay(StringorderNo,doubleamount){// 1. 项目内部参数订单号、金额转换为第三方SDK需要的参数JSONStringorderInfo{\orderNo\:\orderNo\,\amount\:amount};// 2. 调用第三方SDK的方法获取XML格式结果StringxmlResultalipaySdk.alipay(orderInfo);// 3. 将XML结果转换为项目内部需要的JSON格式StringjsonResultxmlToJson(xmlResult,orderNo);// 4. 返回项目内部规范的结果returnjsonResult;}// 模拟XML转JSON实际开发中使用FastJSON、Jackson等工具privateStringxmlToJson(Stringxml,StringorderNo){return{\orderNo\:\orderNo\,\status\:\success\,\message\:\支付成功\};}}步骤4客户端通过Spring调用企业实战版通过Spring的ApplicationContext获取目标接口实例直接调用方法无需关心适配细节和实例创建。importorg.springframework.context.ApplicationContext;importorg.springframework.context.annotation.AnnotationConfigApplicationContext;importorg.springframework.context.annotation.ComponentScan;// Spring配置类扫描组件替代XML配置避免链接失效ComponentScan(com.example.adapter)publicclassSpringConfig{}// 客户端调用企业开发常用publicclassSpringAdapterClient{publicstaticvoidmain(String[]args){// 加载Spring配置获取容器注解配置无XML避免链接失效ApplicationContextcontextnewAnnotationConfigApplicationContext(SpringConfig.class);// 获取目标接口实例Spring自动注入适配器PayServicepayServicecontext.getBean(PayService.class);// 调用项目内部接口底层自动通过适配器对接第三方SDKStringresultpayService.pay(ORDER20260415004,299.9);System.out.println(项目内部支付结果result);}}进阶要点企业开发避坑优先使用注解配置Component、Autowired、ComponentScan替代XML配置避免XML中出现“link dead”链接失效问题适配器与适配者通过Spring依赖注入关联便于后续替换适配者如将支付宝SDK替换为微信SDK只需修改适配器注入的适配者可结合Spring的Qualifier注解实现多个适配者的灵活切换如同时对接支付宝、微信SDK通过注解指定适配者若必须使用XML配置避免使用外部链接可替换为本地XSD文件或升级Spring依赖版本确保配置文件正常加载。五、实战三框架中的适配器模式面试必说适配器模式在Java主流框架中应用极其广泛面试时能说出1-2个具体应用会显得你理解更深刻不是只懂理论。1. Spring 框架HandlerAdapter核心应用Spring MVC中的HandlerAdapter是适配器模式的经典实现核心作用是“适配不同类型的处理器Controller”让DispatcherServlet前端控制器无需关心不同处理器的调用方式统一通过适配器调用。Target目标接口HandlerAdapter接口定义了handleRequest()方法统一处理器调用规范Adaptee适配者不同类型的处理器如Controller、HttpRequestHandler、ServletAdapter适配器具体适配器如SimpleControllerHandlerAdapter、HttpRequestHandlerAdapter适配不同类型的处理器。原理DispatcherServlet通过HandlerAdapter找到对应的适配器调用适配器的handleRequest()方法适配器再调用具体处理器的方法实现不同处理器的统一调用完美解决“处理器类型多样、接口不统一”的问题。2. Spring 框架MethodInterceptorAOP中的适配器Spring AOP中的MethodInterceptor方法拦截器本质也是接口适配器的实现。Spring AOP的拦截器链中不同的拦截器可能实现不同的接口通过适配器模式将不同拦截器的接口转换为统一的拦截器接口实现拦截器的链式调用。3. Java 内置适配器InputStreamReader/OutputStreamWriterJava IO流中的InputStreamReader字节流转字符流、OutputStreamWriter字符流转字节流是JDK内置的适配器模式实现日常开发中经常用到。InputStreamReader将InputStream字节流适配者适配为Reader字符流目标接口OutputStreamWriter将OutputStream字节流适配者适配为Writer字符流目标接口。// 示例InputStreamReader字节流转字符流适配器模式InputStreaminputStreamnewFileInputStream(test.txt);// InputStreamReader是适配器将字节流适配为字符流ReaderreadernewInputStreamReader(inputStream,StandardCharsets.UTF_8);4. 实际项目场景日志框架适配项目中原有日志框架使用Log4j后来需要升级为SLF4JLogback为了不修改原有项目中所有Log4j的调用代码使用适配器模式实现Log4j接口到SLF4J接口的适配让原有代码无需修改即可使用新的日志框架。六、适配器模式面试高频考点必背1. 适配器模式的核心优势解耦隔离原有接口和调用方无需修改原有代码和调用方代码实现接口兼容遵循开闭原则新增适配场景时只需新增适配器类无需修改原有代码提高复用性复用原有接口的功能无需重复开发降低维护成本灵活性高可灵活适配不同接口适配者或目标接口变化时只需修改适配器。2. 适配器模式的三种实现方式及区别高频中的高频核心区别适配方式不同继承 vs 持有引用 vs 抽象类空实现结合表格记忆面试直接说清类适配器继承适配者实现目标接口受单继承限制耦合度高对象适配器持有适配者引用实现目标接口无继承限制耦合度低企业常用接口适配器抽象类实现目标接口空实现所有方法适用于目标接口方法过多的场景。3. 适配器模式和装饰者模式的区别面试易错点核心区别目的不同记住一句话即可适配器模式核心是“解决接口不兼容”让两个不相关的接口可以对接不改变原有功能装饰者模式核心是“增强原有功能”在不改变原有接口的前提下为对象增加新的功能。补充适配器模式是“转换接口”装饰者模式是“增强功能”两者都不修改原有代码但目的和使用场景完全不同。4. 适配器模式的缺点避坑重点增加系统复杂度新增适配器类会增加类的数量后续维护成本略有增加适配逻辑复杂当适配者和目标接口差异较大时适配器的转换逻辑会比较复杂容易出错可读性降低调用方看不到适配细节若适配器逻辑不清晰后续排查问题难度较大。5. 什么时候用适配器模式实战判断旧系统接口无法修改需要对接新系统接口如旧订单接口适配新前端对接第三方SDK第三方接口规范与项目内部接口不兼容如支付SDK、日志SDK系统中存在多个接口规范不一致但功能相似的模块需要统一调用方式目标接口方法过多调用方只需使用部分方法使用接口适配器。七、总结实战面试双达标对于Java后端开发者来说适配器模式是“解决接口兼容问题的万能钥匙”它不像工厂模式、单例模式那样频繁使用但在系统集成、第三方对接、旧系统维护场景中是不可或缺的设计模式也是面试中高频考察的重点尤其是三种实现方式的区别。基础掌握3个核心角色理解适配器模式“接口转换”的核心思想能手动实现三种适配器实战重点掌握对象适配器企业常用结合Spring依赖注入实现适配规避XML链接失效的坑面试能清晰区分三种适配器的区别说出框架中的应用场景掌握与装饰者模式的区别避坑记住“接口不兼容用适配器功能增强用装饰者”避免过度设计如接口兼容可直接修改代码时无需使用适配器。记住一句话适配器模式的核心是“兼容不修改转换无感知”通过中间适配器让不兼容的接口无缝对接既保证原有系统稳定又满足新的业务需求。掌握它能让你在系统集成、旧系统维护中事半功倍面试时也能轻松应对设计模式相关提问。补充常见问题解决问题1适配者和目标接口差异过大适配器逻辑复杂怎么办解决拆分适配器将复杂的转换逻辑拆分为多个小适配器或提取公共转换方法降低维护难度问题2Spring XML配置中出现“link dead”链接失效怎么办解决优先使用注解配置Component、ComponentScan若必须用XML替换为本地Spring XSD文件或升级Spring依赖版本问题3如何实现多个适配者的灵活切换解决结合Spring的Qualifier注解注入不同的适配者或使用工厂模式根据条件动态创建对应的适配器。

更多文章