深入理解Java适配器模式,彻底搞懂设计思想
前言在之前的一篇博客中我们详细探讨了Java代理模式——这种通过中间代理类控制目标对象访问、并对核心方法进行增强的设计模式在Spring AOP、权限控制等场景中发挥着至关重要的作用。代理模式的核心是“不改变目标接口只做功能增强”让我们在不侵入原有代码的前提下灵活扩展业务逻辑。而今天我们要聊的是另一种同样常用的结构型设计模式——适配器模式它与代理模式有着相似的“中间类”结构却承载着完全不同的核心使命如果说代理模式是“给原有功能加buff”那么适配器模式就是“让原本不相容的功能能并肩作战”。接下来我们就详细展开对适配器模式的讲解一、什么是适配器模式1.定义适配器模式Adapter Pattern是结构型设计模式的一种核心作用是让两个原本不兼容的接口 / 类能够协同工作就像生活中的电源适配器国内电器是两脚插头国外插座是三孔插孔通过电源适配器就能让两者匹配使用无需修改电器或插座本身。2.本质在 Java 开发中适配器模式的本质是通过一个中间适配器类将目标类的接口转换成客户端期望的接口解决接口不兼容问题同时遵循开闭原则对扩展开放对修改关闭。3.核心角色适配器模式包含 4 个核心角色缺一不可目标接口Target客户端期望使用的接口是业务的标准规范比如「两脚充电接口」。适配者Adaptee已存在的、功能可用但接口不兼容的类比如「三孔电源插座」是我们需要适配的对象。适配器Adapter核心中间类实现 Target 接口内部持有 Adaptee 对象负责将 Target 的调用转换为 Adaptee 的调用。客户端Client使用 Target 接口的业务方只和目标接口交互无需关心适配器和适配者。4.设计思想不修改原有代码适配者和目标接口的源码都不改动符合开闭原则避免修改旧代码引发 bug。解耦客户端只依赖目标接口和适配者完全解耦扩展性极强。复用让已有的、功能完善的旧类能在新业务中继续使用避免重复开发。二、适配器模式的两种实现方式Java 中适配器模式有两种经典实现类适配器继承实现和对象适配器组合实现开发中优先使用对象适配器符合组合优于继承原则。1. 类适配器继承实现1经典类适配器---功能合并结构拆解A 类适配者 Adaptee已有类提供了m1()方法但没有实现 B 接口。B 接口目标接口 Target客户端期望的接口定义了m1()和m2()两个方法。适配器继承 A 类 实现 B 接口把 A 类的m1()功能合并到 B 接口中同时补充实现m2()方法让客户端可以通过 B 接口调用 A 类的功能。核心作用接口兼容转换把已有类A的功能适配成客户端期望的接口B让两者可以协同工作。功能合并复用通过继承 A 类复用m1()通过实现 B 接口满足客户端的调用规范同时补充m2()方法。核心目标是解决接口不兼容问题让原本无法一起工作的类和接口适配起来。代码示例// 1. 适配者A类已有功能但接口不兼容 class A { public void m1() { System.out.println(A类的m1()方法已有的功能); } } // 2. 目标接口B客户端期望的标准接口 interface B { void m1(); void m2(); } // 3. 类适配器继承A类 实现B接口完成适配 class Adapter extends A implements B { // 继承A类自动拥有了m1()方法直接复用 Override public void m2() { // 补充实现目标接口的m2()方法 System.out.println(适配器补充实现的m2()方法); } } // 客户端只依赖目标接口B public class Client { public static void main(String[] args) { B target new Adapter(); target.m1(); // 调用的是A类的m1()方法 target.m2(); // 调用的是适配器实现的m2()方法 } }2默认适配器---只想实现接口的指定类结构拆解目标接口定义了m1()~m10()共 10 个方法是一个多方法的通用接口。适配器实现了这个接口把m1()~m10()所有方法都做了空实现比如public void m1() {}。业务类继承适配器只重写自己关心的m1()方法其他 9 个方法直接继承适配器的空实现不用自己写。核心作用简化接口实现避免了 “实现接口必须重写所有方法” 的冗余代码。适配的是「接口和实现者」之间的关系让实现者只关注自己需要的方法。代码示例// 1. 接口方法很多业务类只需要用其中一个 interface Target { void m1(); void m2(); // ... 省略中间方法 void m10(); } // 2. 适配器实现接口所有方法空实现 abstract class Adapter implements Target { Override public void m1() {} Override public void m2() {} // ... 其他方法也都空实现 Override public void m10() {} } // 3. 业务类继承适配器只重写需要的方法 class MyClass extends Adapter { Override public void m1() { // 只在这里写业务逻辑其他方法不用管 System.out.println(我只关心m1()方法); } }2.对象适配器组合实现推荐在类适配器里面的第一种经典类适配器A类和B接口里面是有一个相同方法名的但如果要合并的是方法名不同但是实现的功能相同我们要怎么做那就可以使用对象适配器结构拆解适配者AdapteeA类含m1()已有类提供了可用的功能但接口不兼容目标接口TargetB接口含mm1()、mm2()客户端期望的标准接口适配器Adapter中间的适配器类核心中间层实现目标接口 B同时持有适配者 A 的对象核心功能基于组合而不是继承适配者通过new对象来持有适配者对象解决了Java单继承限制类适配器受到Java单继承的限制无法继承其他类而对象适配器没有这个问题可以同时持有多个适配器对象适配多个不同的类3.对比维度对象适配器类适配器实现方式实现目标接口 持有适配者对象组合继承适配者 实现目标接口继承Java 单继承限制无限制可同时适配多个类受限制只能继承一个适配者耦合度低适配器与适配者松耦合高适配器与适配者强耦合扩展性强可通过构造方法传入不同适配者弱适配者固定为继承的类推荐程度✅ 实际开发首选❌ 仅在特殊场景使用三、适配器模式 VS 代理模式适配器模式和代理模式都属于结构型模式且都有「中间类」很多人容易混淆这里讲一下核心区别目的不同、作用不同、关注点不同。1. 核心目的适配器模式解决「接口不兼容」问题让两个无法协同的类能一起工作不新增功能只做转换。代理模式解决「对象访问控制」问题不改变目标接口对目标对象的方法进行增强 / 控制如日志、事务、权限。2. 核心关系适配器模式适配器 → 适配者转换关系目标接口和适配者接口不兼容。代理模式代理类 → 目标对象代理关系代理类和目标对象实现同一个接口接口完全一致。3. 功能侧重点适配器模式接口转换不修改、不增强原有功能只做兼容。代理模式功能增强 / 访问控制在目标方法执行前后添加额外逻辑接口不变。4. 代码层面区别适配器实现目标接口内部调用适配者的不同方法接口不兼容。代理实现目标接口内部调用目标对象的相同方法接口一致。