代理模式和适配器模式区别 代理模式 vs 适配器模式 完整详解核心区别一、先分别吃透两种模式本质1. 适配器模式Adapter结构型核心目的做「转换兼容」两个已有接口不匹配通过适配器做一层接口转换让原本不能协作的两类代码一起工作。关键词兼容、翻译、转换、统一接口角色目标接口 Target我方程序期望调用的统一接口被适配者 Adaptee已有旧接口/第三方类方法名、参数和我方不匹配适配器 Adapter包装 Adaptee实现 Target内部做参数、方法转发转换类比转换插头插座是标准Target国标三脚国外电器是Adaptee美标两脚适配器插头做转换让电器插上插座。两种实现类适配器继承被适配类 实现目标接口Java单继承很少用对象适配器组合持有被适配对象主流2. 代理模式Proxy结构型核心目的做「控制增强」用一个代理类包装原始目标对象对外暴露完全一模一样的接口不修改原有接口定义在方法前后增加额外逻辑权限、缓存、日志、延时创建、远程调用。关键词控制、增强、拦截、中介、透明访问角色抽象主题 Subject原始对象与代理共同实现的统一接口真实对象 RealSubject业务核心对象代理 Proxy实现同一接口持有真实对象拦截所有方法类比中介/经纪人明星RealSubject和经纪人Proxy对外联系方式完全一致都能接演出但经纪人会做前置筛选报价、后置签合同明星只负责表演。常见分类静态代理手写代理类动态代理JDK动态代理、CGLIBSpring AOP底层就是动态代理远程代理、虚拟代理、保护代理、缓存代理二、最核心本质差异一句话区分适配器接口不一样做转换解决「不兼容」对外提供新接口抹平新旧/第三方接口差异使用者看不到原始类接口。代理接口完全一样做增强控制访问对外和原始对象完全相同接口使用者无感知透明调用只附加额外逻辑。关键分水岭接口是否改变调用方期望的接口 ≠ 原始类接口 → 适配器调用方期望的接口 原始类接口 → 代理三、分层详细对比1. 设计目标不同适配器打通两套不兼容接口复用已有第三方/旧代码不用修改原有代码。场景老系统接口、第三方SDK、不同组件整合。代理在不改动原对象代码前提下对原有功能进行拦截、增强、管控。场景权限校验、日志、缓存、懒加载、远程RPC、事务、限流。2. 对外暴露接口适配器适配器实现全新的目标接口Target内部调用被适配者Adaptee的异构方法。外部调用Adapter的是一套新方法名和Adaptee方法名完全不同。示例Targetpay(amount)Adaptee第三方支付transferMoney(money, channel)Adapter内部pay()里转换参数调用transferMoney()代理代理和真实对象实现同一个接口Subject方法名、参数、返回值完全一致。外部调用Proxy的方法和直接调用RealSubject无任何区别。示例统一接口queryOrder(Long id)Proxy.queryOrder() 先查缓存无缓存再调用 RealSubject.queryOrder()3. 与被包装对象的关系适配器被包装的 Adaptee 是外部/老旧异构组件本来就不适合当前系统调用规范两者设计初衷无关。适配器是翻译官。代理被包装的 RealSubject 是系统核心业务对象本身完全满足业务功能只是需要附加控制逻辑。代理是保镖/中介。4. 是否改变调用行为适配器核心逻辑委托给被适配者适配器只做参数、格式转换不新增额外横切逻辑。没有前置校验、缓存、日志这类增强仅做格式翻译。代理核心逻辑依然委托真实对象但会增加前后附加逻辑前置权限校验、参数校验、缓存查询后置日志记录、耗时统计、事务提交5. 代码示例直观区分示例1适配器接口不匹配转换// 目标接口系统统一支付标准publicinterfacePayment{voidpay(BigDecimalmoney);}// 第三方老支付SDK接口不兼容publicclassOldThirdPay{publicvoidtransfer(BigDecimalcash,Integertype){System.out.println(第三方转账cash);}}// 适配器统一转换成Payment接口publicclassPayAdapterimplementsPayment{privateOldThirdPayadapteenewOldThirdPay();Overridepublicvoidpay(BigDecimalmoney){// 转换参数适配第三方接口adaptee.transfer(money,1);}}// 使用业务只认Payment完全不知道OldThirdPay存在PaymentpaynewPayAdapter();pay.pay(newBigDecimal(100));示例2代理接口完全一致增加缓存增强// 统一抽象接口publicinterfaceOrderService{OrdergetOrder(Longid);}// 真实业务类publicclassOrderServiceImplimplementsOrderService{OverridepublicOrdergetOrder(Longid){// 查询数据库returndb.selectById(id);}}// 缓存代理接口完全一致publicclassOrderProxyimplementsOrderService{privateOrderServicerealnewOrderServiceImpl();privateMapLong,OrdercachenewHashMap();OverridepublicOrdergetOrder(Longid){// 前置增强缓存拦截if(cache.containsKey(id)){returncache.get(id);}Orderorderreal.getOrder(id);// 后置增强放入缓存cache.put(id,order);returnorder;}}// 使用方式和原对象完全一致上层无感知OrderServiceservicenewOrderProxy();service.getOrder(1001L);6. 适用业务场景对比适配器典型场景整合第三方SDK微信/支付宝老版本接口、OSS存储、第三方短信新旧系统兼容老数据库DAO、遗留接口适配新业务框架统一多类异构工具多种日志组件、多种文件存储适配统一工具类不同协议转换HTTP接口适配内部RPC调用格式代理典型场景动态代理做AOP日志、切面、事务、限流、权限校验Spring核心缓存代理数据库查询加本地缓存、Redis缓存虚拟代理大图片/大文件延迟加载用到才创建真实对象保护代理接口访问权限校验未登录拦截调用远程代理RPC客户端代理屏蔽网络通信细节Dubbo/Feign静态代理封装工具类增加统一异常处理7. 优缺点与取舍适配器优点快速兼容异构代码无需修改第三方/遗留类统一对外调用标准。缺点仅做转换无法统一增加横切增强逻辑。代理优点无侵入增强原有业务上层调用零改动动态代理可统一批量切面处理。缺点只能用于接口匹配的场景不能解决接口不兼容问题。四、表格汇总对比对比维度适配器模式Adapter代理模式Proxy核心职责接口转换、兼容异构组件透明拦截、功能增强、访问控制对外接口提供全新目标接口与被包装对象接口不同和原始对象完全相同接口方法签名一致设计关系解决「接口不匹配」问题不改变接口附加额外逻辑类比转换插头、翻译官经纪人、中介、保镖包装对象第三方/遗留异构类系统原生标准业务对象附加逻辑仅参数/格式转换无横切增强缓存、日志、权限、事务、延时加载上层感知上层只依赖Target看不到原始类上层完全无感知像直接调用原对象典型框架应用SpringMVC HandlerAdapter、集合工具适配Spring AOP动态代理、Dubbo远程代理五、极易混淆的边界场景区分技巧场景1如果我包装后方法名变了 → 适配器需要把sendMsg(String phone,String content)改成系统统一sendSms(SmsDTO dto)参数结构重组一定是适配器。场景2包装后方法名、参数完全不变只是加日志/缓存 → 代理getUser(Long id)前后加打印耗时、缓存查询接口一丝不动一定是代理。场景3同时需要转换增强怎么处理分层组合外层代理做增强内层适配器做转换调用链路Controller → Proxy日志/权限→ Adapter接口转换→ 第三方SDK六、一句话记忆口诀接口不一样要兼容用适配器接口一模一样要增强用代理。