【软考设计模式】适配器模式:接口兼容的“翻译官“与代码填空精讲

发布时间:2026/7/9 3:28:29

【软考设计模式】适配器模式:接口兼容的“翻译官“与代码填空精讲 系列定位软考软件设计师 / 系统架构设计师 — 结构型模式专题第 1 讲考察分值上午题 1-2 分下午题常作为代码填空或类图识别出现难度等级⭐⭐⭐☆☆概念直观但类适配器 vs 对象适配器的区分是高频失分点一、考纲定位与模式定义1.1 考纲要求适配器模式在软考中属于结构型模式的基础内容。考察形式包括上午选择题判断场景描述所属模式识别类适配器继承与对象适配器组合/聚合的实现差异判断适配器模式与桥接模式、装饰模式的区别下午设计题补全适配器类的适配逻辑将目标接口方法委托给被适配者的方法补全类适配器中的继承关系或对象适配器中的组合关系识别类图角色Target、Adapter、Adaptee1.2 模式定义适配器模式将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。核心意图当现有类Adaptee的接口与客户端期望的接口Target不一致时不修改原有代码而是通过一个适配器Adapter来做接口转换让原本不兼容的接口能够协同工作。就像出国旅行时带的电源转换插头 —— 不改变你的电器也不改变当地的插座只加一个中间转换层。二、UML 类图与角色划分2.1 对象适配器组合/聚合更常用┌─────────────────────────────────────────────────────────┐ │ Client │ │ main() │ │ ├── Target target new Adapter(adaptee); │ │ └── target.request(); // 调用的是适配后的方法 │ └───────────────────────┬─────────────────────────────────┘ │ uses ▼ ┌─────────────────────────────────────────────────────────┐ │ interface │ │ Target │ │ request() │ └───────────────────────┬─────────────────────────────────┘ △ implements │ ▼ ┌─────────────────────────────────────────────────────────┐ │ Adapter │ │ - adaptee: Adaptee ◄── 组合/聚合关系 │ │ request() { │ │ adaptee.specificRequest(); // 委托调用 │ │ } │ └─────────────────────────────────────────────────────────┘ │ uses ▼ ┌─────────────────────────────────────────────────────────┐ │ Adaptee │ │ specificRequest() │ │ // 已有的接口但与 Target 不兼容 │ └─────────────────────────────────────────────────────────┘2.2 类适配器继承较少用┌─────────────────────────────────────────────────────────┐ │ Client │ │ main() │ │ ├── Target target new Adapter(); │ │ └── target.request(); │ └───────────────────────┬─────────────────────────────────┘ │ uses ▼ ┌─────────────────────────────────────────────────────────┐ │ interface │ │ Target │ │ request() │ └───────────────────────┬─────────────────────────────────┘ △ implements │ ▼ ┌─────────────────────────────────────────────────────────┐ │ Adapter │ │ ── extends ──▶ Adaptee ◄── 继承关系 │ │ request() { │ │ specificRequest(); // 直接调用父类方法 │ │ } │ └─────────────────────────────────────────────────────────┘ △ │ extends ▼ ┌─────────────────────────────────────────────────────────┐ │ Adaptee │ │ specificRequest() │ └─────────────────────────────────────────────────────────┘角色职责软考填空关键词Target目标接口客户端期望的接口定义客户端需要调用的方法interfacerequest()Adaptee被适配者已经存在的、功能正确但接口不兼容的类现有类方法名与 Target 不同Adapter适配器将 Adaptee 的接口转换为 Target 的接口implements Target 组合/继承 AdapteeClient客户端面向 Target 接口编程通过 Adapter 间接使用 AdapteeTarget target new Adapter(...)类图识别要点软考类图中适配器模式一定有Target 接口、Adaptee 现有类、Adapter 转换类三个角色。关键看 Adapter 与 Adaptee 的关系如果是实线空心菱形组合/聚合→ 对象适配器如果是实线空心三角继承→ 类适配器。三、场景一对象适配器 —— 第三方库接口适配最常用业务背景系统已经定义了统一的日志接口LoggerTarget要求所有日志组件都实现log(String msg)方法。现在引入了一个第三方日志库ThirdPartyLoggerAdaptee它提供的方法是writeLog(String content)接口不兼容。不能修改第三方库的源码也不能修改系统已有的Logger接口。使用对象适配器解决。说明对象适配器通过组合/聚合持有 Adaptee 的引用在 Adapter 内部将 Target 的方法调用转换为 Adaptee 的方法调用。这是实际开发中最常用的方式因为 Java 不支持多继承类适配器只能继承一个类而对象适配器可以适配多个 Adaptee。3.1 代码实现// Target系统统一的日志接口 public interface Logger { void log(String message); } // Adaptee第三方日志库不可修改源码 public class ThirdPartyLogger { public void writeLog(String content) { System.out.println([第三方日志] content); } } // Adapter对象适配器组合 Adaptee public class ThirdPartyLoggerAdapter implements Logger { private ThirdPartyLogger adaptee; public ThirdPartyLoggerAdapter(ThirdPartyLogger adaptee) { this.adaptee adaptee; } Override public void log(String message) { adaptee.writeLog(message); } } // Client客户端面向 Target 接口编程 public class Client { public static void main(String[] args) { Logger logger new ThirdPartyLoggerAdapter(new ThirdPartyLogger()); logger.log(系统启动成功); } }关键体会客户端完全不知道ThirdPartyLogger的存在它只面向Logger接口编程。Adapter 像 翻译官 一样把log()翻译 成writeLog()。第三方库的源码没有被修改系统的Logger接口也没有被修改双方通过 Adapter 和解。四、场景二类适配器 —— 遗留系统接口兼容继承方式业务背景系统有一个遗留的LegacyPrinter类Adaptee提供printOld(String text)方法。新系统定义了Printer接口Target要求实现print(String document)方法。由于LegacyPrinter是类而不是接口且新系统需要直接复用LegacyPrinter的部分内部逻辑使用类适配器通过继承来适配。说明类适配器通过继承Adaptee 类来实现适配。优点是可以直接访问 Adaptee 的 protected 方法缺点是受限于单继承Java 中 Adapter 只能继承一个 Adaptee 类。软考中如果考到类适配器通常会在题干中明确说明 通过继承实现。4.1 代码实现// Target新系统统一的打印机接口 public interface Printer { void print(String document); } // Adaptee遗留打印机类不可修改源码 public class LegacyPrinter { public void printOld(String text) { System.out.println([遗留打印机] 正在打印: text); } protected void warmUp() { System.out.println([遗留打印机] 预热中...); } } // Adapter类适配器继承 Adaptee public class LegacyPrinterAdapter extends LegacyPrinter implements Printer { Override public void print(String document) { warmUp(); printOld(document); } } // Client客户端面向 Target 接口编程 public class Client { public static void main(String[] args) { Printer printer new LegacyPrinterAdapter(); printer.print(2024年年度报告.pdf); } }关键体会类适配器通过继承获得了 Adaptee 的所有非 private 方法可以直接调用warmUp()等 protected 方法。这是类适配器相对于对象适配器的优势。但代价是失去了灵活性 —— 如果以后需要适配另一个品牌的遗留打印机必须再写一个 Adapter 类。五、场景三双向适配器 —— 两个不兼容系统的互操作高级场景业务背景系统 A 使用SystemAInterface接口系统 B 使用SystemBInterface接口两个系统需要互相调用对方的功能但接口完全不兼容。使用双向适配器让两个系统可以互相适配。说明双向适配器是适配器模式的一种扩展一个 Adapter 同时实现两个接口内部持有两个系统的引用实现双向转换。软考中此场景出现频率较低但上午选择题可能作为 适配器模式变体 出现。5.1 代码实现// 系统 A 的接口 public interface SystemAInterface { void methodA(); } // 系统 B 的接口 public interface SystemBInterface { void methodB(); } // 系统 A 的具体实现 public class SystemA implements SystemAInterface { Override public void methodA() { System.out.println(系统 A 执行 methodA); } } // 系统 B 的具体实现 public class SystemB implements SystemBInterface { Override public void methodB() { System.out.println(系统 B 执行 methodB); } } // 双向适配器 public class TwoWayAdapter implements SystemAInterface, SystemBInterface { private SystemAInterface systemA; private SystemBInterface systemB; public TwoWayAdapter(SystemAInterface systemA, SystemBInterface systemB) { this.systemA systemA; this.systemB systemB; } Override public void methodA() { if (systemA ! null) { systemA.methodA(); } else { System.out.println(通过适配器用系统 B 的能力模拟 methodA); systemB.methodB(); } } Override public void methodB() { if (systemB ! null) { systemB.methodB(); } else { System.out.println(通过适配器用系统 A 的能力模拟 methodB); systemA.methodA(); } } } // Client public class Client { public static void main(String[] args) { SystemA systemA new SystemA(); SystemB systemB new SystemB(); TwoWayAdapter adapter new TwoWayAdapter(systemA, systemB); SystemAInterface aClient adapter; aClient.methodA(); SystemBInterface bClient adapter; bClient.methodB(); } }六、三种场景对比与演进思路维度场景一对象适配器场景二类适配器场景三双向适配器Adapter 与 Adaptee 关系组合/聚合持有引用继承extends组合持有两个系统引用Java 支持度✅ 完全支持⚠️ 单继承限制✅ 完全支持Adaptee 数量可适配多个 Adaptee只能适配一个 Adaptee可适配多个访问 Adaptee 内部只能访问 public 方法可访问 protected 方法只能访问 public 方法灵活性高运行时注入 Adaptee低编译时绑定中典型应用第三方库适配、多 Adaptee 场景复用遗留类 protected 方法两个系统互操作软考出现频率⭐⭐⭐⭐⭐⭐⭐⭐⭐软考核心结论软考中如果没有特别说明默认考察对象适配器组合方式。如果题干明确提到 继承 或 无法修改 Adaptee 源码但需要访问其 protected 方法才考虑类适配器。七、软考高频考点与易混淆辨析7.1 高频考点考点内容模式分类结构型模式GoF 23 正式成员核心三角色Target目标接口、Adaptee被适配者、Adapter适配器对象适配器Adapter组合/聚合Adaptee将 Target 方法委托给 Adaptee 方法类适配器Adapter继承Adaptee直接调用父类方法设计原则开闭原则不修改 Target 和 Adaptee 的源码只新增 Adapter适用场景① 使用现有类但接口不匹配 ② 需要复用多个子类但接口不一致 ③ 需要统一多个类的接口与桥接模式区别适配器事后补救接口不兼容时的转换桥接事前设计将抽象与实现分离与装饰模式区别适配器转换接口不改变功能装饰增强功能不改变接口7.2 易混淆辨析适配器 vs 桥接 vs 装饰对比项适配器模式桥接模式装饰模式目的转换接口让不兼容的接口能一起工作分离抽象与实现让它们独立变化增强功能给对象添加额外职责是否改变接口✅ 改变Target → Adaptee 转换❌ 不改变抽象接口与实现接口一致❌ 不改变保持原接口增加功能是否改变功能❌ 不改变只是转换调用方式❌ 不改变只是分离层次✅ 改变增加新功能设计时机事后补救已有代码不兼容事前设计架构层面分离运行时动态增强类图特征有 Adapter 类做转换有 Abstraction 和 Implementor 两个独立层次有 Decorator 继承/组合 Component软考考法代码填空补全 Adapter 转换逻辑类图识别两个独立继承树类图识别递归组合结构记忆口诀适配器是 翻译官接口转换桥接是 分层设计抽象与实现分离装饰是 套娃层层增强功能。八、真题风格模拟与代码填空模拟题 1上午选择题以下关于适配器模式的叙述中正确的是。A. 适配器模式属于创建型模式主要用于创建兼容的接口B. 在对象适配器中Adapter 通过继承 Adaptee 来实现接口转换C. 适配器模式可以在不修改原有类源码的前提下使接口不兼容的类能够协同工作D. 适配器模式与装饰模式的核心区别在于适配器模式增强对象功能装饰模式转换接口答案C解析A 错误适配器属于结构型模式不是创建型。B 错误对象适配器通过组合/聚合持有引用Adaptee类适配器才通过继承。C 正确这是适配器模式的核心价值 —— 不修改原有代码开闭原则只新增 Adapter 做转换。D 错误说反了。适配器模式转换接口不改变功能装饰模式增强功能不改变接口。模拟题 2下午代码填空 — 对象适配器某系统定义了统一的支付接口PaymentTarget要求实现pay(double amount)方法。现有遗留类OldPaymentGatewayAdaptee提供makePayment(String amount)方法。请使用对象适配器补全1~4。// Target统一支付接口 interface Payment { void pay(double amount); } // Adaptee遗留支付网关 class OldPaymentGateway { public void makePayment(String amount) { System.out.println(旧网关支付: ¥ amount); } } // Adapter对象适配器 class PaymentAdapter (1)______ Payment { private (2)______ adaptee; public PaymentAdapter((3)______ adaptee) { this.adaptee adaptee; } Override public void pay(double amount) { adaptee.(4)______(String.valueOf(amount)); } } public class Client { public static void main(String[] args) { Payment payment new PaymentAdapter(new OldPaymentGateway()); payment.pay(199.99); } }答案(1)implements(2)OldPaymentGateway(3)OldPaymentGateway(4)makePayment阅卷要点(1) 必须是implements因为对象适配器实现 Target 接口如果是类适配器则是extends Adaptee implements Target。(2)(3) 必须是OldPaymentGatewayAdaptee 类型体现组合关系。(4) 必须是makePayment这是 Adaptee 的方法名Adapter 负责将 Target 的pay()转换为 Adaptee 的makePayment()。模拟题 3下午代码填空 — 类适配器某系统新定义了MediaPlayer接口Target要求实现play(String filename)方法。现有遗留类AdvancedMediaPlayerAdaptee提供playMp4(String file)和playVlc(String file)方法。请使用类适配器补全1~3。// Target统一播放器接口 interface MediaPlayer { void play(String filename); } // Adaptee高级播放器支持 MP4 和 VLC class AdvancedMediaPlayer { public void playMp4(String file) { System.out.println(播放 MP4: file); } public void playVlc(String file) { System.out.println(播放 VLC: file); } } // Adapter类适配器 class MediaAdapter (1)______ AdvancedMediaPlayer (2)______ MediaPlayer { Override public void play(String filename) { if (filename.endsWith(.mp4)) { (3)______(filename); } else if (filename.endsWith(.vlc)) { playVlc(filename); } } } public class Client { public static void main(String[] args) { MediaPlayer player new MediaAdapter(); player.play(movie.mp4); } }答案(1)extends(2)implements(3)playMp4阅卷要点(1) 必须是extends类适配器通过继承 Adaptee 获得其方法。(2) 必须是implements类适配器同时实现 Target 接口。(3) 必须是playMp4因为类适配器继承了 Adaptee可以直接调用父类方法不需要adaptee.playMp4()因为this就是 Adaptee 的子类。注意类适配器中调用父类方法可以直接写方法名也可以写super.playMp4(filename)两者都算对。但adaptee.playMp4()是对象适配器的写法在类适配器中会扣分因为类适配器没有adaptee字段。九、常见陷阱与注意事项陷阱 1误认为适配器是创建型模式适配器属于结构型模式因为它解决的是类与类之间的结构关系接口转换而不是对象的创建问题。软考上午题如果问以下属于创建型模式的是选项里出现适配器不能选。陷阱 2对象适配器 vs 类适配器混淆这是软考最高频的混淆点对象适配器implements Targetprivate Adaptee adaptee组合关系类适配器extends Adaptee implements Target继承关系快速判断技巧看 Adapter 类图里与 Adaptee 的关系线。如果是带空心菱形的实线组合/聚合→ 对象适配器如果是带空心三角的实线继承→ 类适配器。陷阱 3类适配器中错误使用对象适配器的调用方式类适配器继承了 Adaptee可以直接调用父类方法如playMp4(filename)或super.playMp4(filename)。如果写成adaptee.playMp4(filename)说明把类适配器当成了对象适配器判卷时会扣分因为类适配器没有adaptee成员变量。陷阱 4与桥接模式混淆桥接模式Bridge也是结构型模式但它的目的是将抽象与实现分离使它们可以独立变化通常有两个独立的继承树如Shape抽象层和Drawing实现层。适配器模式只有一个 Adaptee 和一个 Target目的是接口转换。如果类图里只有一个转换类Adapter连接两个不兼容的接口 → 适配器如果有两个独立的层次结构通过组合关联 → 桥接。陷阱 5与装饰模式混淆装饰模式Decorator也是结构型模式包装一个对象以添加新功能但保持接口不变如BufferedReader包装Reader。适配器模式是改变接口以兼容不同接口不添加新功能。如果类图里 Adapter 的方法名与 Target 一致但内部调用了不同的 Adaptee 方法 → 适配器如果 Decorator 新增了方法或增强了原有方法的功能 → 装饰。陷阱 6Adapter 的构造器参数对象适配器的构造器通常需要传入 Adaptee 实例如new Adapter(new Adaptee())因为 Adapter 通过组合持有 Adaptee。类适配器通常不需要构造器参数直接new Adapter()因为 Adapter 自身就是 Adaptee 的子类。软考代码填空时如果看到 Adapter 构造器有参数 → 对象适配器无参数 → 类适配器。十、总结要点内容定义将一个类的接口转换成客户希望的另外一个接口使原本不兼容的类能协同工作分类结构型模式GoF 23 正式成员核心三角色Target目标接口、Adaptee被适配者、Adapter适配器对象适配器Adapter组合Adaptee将 Target 方法委托给 Adaptee 方法类适配器Adapter继承Adaptee直接调用父类方法设计原则开闭原则不修改 Target 和 Adaptee只新增 Adapter与桥接模式区别适配器事后补救接口转换桥接事前设计抽象与实现分离与装饰模式区别适配器转换接口不改变功能装饰增强功能不改变接口软考重点对象适配器 vs 类适配器的区分代码填空补全 Adapter 的转换逻辑类图角色识别答题技巧看到接口不兼容、需要转换接口、已有类但接口不匹配 → 适配器看到组合关系 → 对象适配器看到继承关系 → 类适配器系列预告下一篇将讲桥接模式—— 当类的抽象和实现都需要独立扩展时如何用 组合代替继承 的方式将两个独立变化的维度解耦避免类爆炸。咱们下回见。

相关新闻