Java 适配器模式(Adapter Pattern)实战解析:基于 java-design-patterns 仓库的完整实现指南
示例工程教程【免费下载链接】java-design-patternsDesign patterns implemented in Java项目地址https://gitcode.com/GitHub_Trending/ja/java-design-patterns点击查看免费下载适配器模式Adapter Pattern又称 Wrapper 包装器是 GoF 结构型设计模式之一其核心价值在于把某个类的接口转换为客户端所期望的另一种接口从而让原本因接口不兼容而无法协作的类能够协同工作。本指南以 java-design-patterns 仓库中的 adapter 模块adapter为范例从模式意图、源码级实现、调用链、测试验证到应用场景与权衡取舍带你在「船长 渔船适配器」的经典场景中完整掌握适配器模式的落地写法。模式概述别名、意图与定义别名También conocido comoWrapper包装器适配器就像给不兼容的类套上一层外壳因此也被称为包装器模式。模式意图Propósito适配器模式的意图十分明确将某个类的接口转换为客户端所期望的另一个接口。通过这种转换原本因接口不匹配而无法协作的类得以一起工作而无需修改任何一方已有的源码。权威定义维基百科对适配器模式的描述可作为标准参照在软件工程中适配器模式是一种软件设计模式它允许将现有类的接口用作另一种接口。它通常用于让现有类在不修改源代码的情况下与其他类协同工作。现实世界类比三个生活化例子适配器模式的思想在现实生活中随处可见理解这些类比有助于把握模式本质读卡器你的存储卡需要把照片传到电脑但存储卡的接口与电脑的 USB 端口不兼容此时读卡器adapter充当了中间适配层让两者连接起来。电源转接头三脚插头无法直接插入两孔插座必须借助电源适配器完成接口转换。翻译员说不同语言的两个人无法直接沟通翻译员将一方的语言「转换」成另一方听得懂的语义。这些例子的共同点正是适配器模式的核心思想不改动两端只新增一个中间转换层。仓库源码级实现完整阅读 adapter 模块模块结构与角色划分java-design-patterns 仓库的 adapter 模块采用 Maven 标准结构源码位于 src/main/java/com/iluwatar/adapter角色类/接口文件目标接口TargetRowingBoatRowingBoat.java被适配者AdapteeFishingBoatFishingBoat.java适配器AdapterFishingBoatAdapterFishingBoatAdapter.java客户端ClientCaptainCaptain.java程序入口AppApp.java从源码注释App.java可以确认故事背景海盗来袭船长只会驾驶划桨船RowingBoat但眼前只有渔船FishingBoat没有时间再造新船于是用适配器模式复用渔船逃生。这正是「复用现有类」这一模式动机的生动写照。第一步定义目标接口与被适配者客户端船长期望的是划桨船接口它定义了唯一的操作row()public interface RowingBoat { void row(); }被适配者FishingBoat是「已有的、想复用但接口不匹配」的类它只会sail()航行并不会row()划桨Slf4j public class FishingBoat { public void sail() { LOGGER.info(The fishing boat is sailing); } }第二步客户端只依赖目标接口船长客户端完全面向RowingBoat接口编程对渔船的存在一无所知。这也体现了适配器模式的解耦价值——客户端不需要知道任何关于适配器的细节public class Captain { private final RowingBoat rowingBoat; // default constructor and setter for rowingBoat public Captain(RowingBoat rowingBoat) { this.rowingBoat rowingBoat; } public void row() { rowingBoat.row(); } }第三步编写适配器把sail()翻译成row()适配器FishingBoatAdapter实现了目标接口RowingBoat内部持有组合一个FishingBoat实例并在row()方法内部转发调用boat.sail()——相当于「翻译官」把客户端的划桨指令翻译成渔船的航行指令Slf4j public class FishingBoatAdapter implements RowingBoat { private final FishingBoat boat; public FishingBoatAdapter() { boat new FishingBoat(); } Override public void row() { boat.sail(); } }第四步在程序入口组装并运行在 App.main 中完成装配把适配器注入船长船长便能驾驶渔船逃出海盗的包围public static void main(final String[] args) { // The captain can only operate rowing boats but with adapter he is able to // use fishing boats as well var captain new Captain(new FishingBoatAdapter()); captain.row(); }程序运行后日志输出来自FishingBoat的sail()10:25:08.074 [main] INFO com.iluwatar.adapter.FishingBoat -- The fishing boat is sailing从源码结构看两种适配器变体从 App.java 的注释可以确认适配器模式存在两种实现变体而本仓库示例采用的是对象适配器类适配器Class Adapter适配器直接继承被适配者类并通过实现目标接口完成适配依赖的是继承单一继承限制下无法同时适配多个类。对象适配器Object Adapter适配器通过组合composition持有被适配者实例再实现目标接口。本仓库的FishingBoatAdapter正是这种写法——它没有继承FishingBoat而是持有一个FishingBoat字段。类图与调用时序从图表看懂模式结构类图Adapter 类图类图清晰地展示了各角色关系对应 adapter.urm.puml 的 PlantUML 定义Captain聚合--RowingBoat接口FishingBoatAdapter实现..|RowingBoat接口FishingBoatAdapter组合--FishingBoat被适配者。调用时序Adapter 时序图时序图直观展示了运行时调用链App.main→Captain.row()→rowingBoat.row()实际调用FishingBoatAdapter.row()→FishingBoat.sail()。整个过程中Captain与FishingBoat互不相识全靠适配器在中间完成接口转换。测试验证如何证明适配器真的把调用「翻译」对了仓库为 adapter 模块提供了两处测试可用于验证实现的正确性也方便读者按图索骥自行运行1. 单元测试验证调用转发AdapterPatternTest.java 使用 Mockito 对被适配对象进行 spy 监控断言船长row()时确实触发了适配器内部对渔船的调用Test void testAdapter() { var captain (Captain) beans.get(ROWING_BEAN); // when captain moves captain.row(); // the captain internally calls the battleship object to move var adapter (RowingBoat) beans.get(FISHING_BEAN); verify(adapter).row(); }该测试还展示了另一种合法的装配方式通过无参构造创建Captain后用setRowingBoat(...)注入适配器对应 Captain.java 上的 LombokSetter、NoArgsConstructor、AllArgsConstructor注解这为依赖注入场景提供了参考。2. 冒烟测试验证程序入口可运行AppTest.java 通过assertDoesNotThrow(() - App.main(new String[] {}))确保整个示例程序能无异常地跑通。运行方式在仓库根目录或 adapter 模块下通过 Maven Wrapper 即可编译与测试./mvnw -pl adapter test需注意使用-pl adapter前建议先./mvnw install构建依赖或直接在根目录执行./mvnw test运行全量测试。何时使用适配器模式根据模式文档localization/es/adapter/README.md当出现以下情况时应当考虑适配器模式想使用一个现有类但其接口与所需接口不匹配——这是最典型的使用场景本仓库的渔船适配即为范例。想创建一个可复用的类与那些本无关联、或当初并未规划要协作的类一起工作这些类不一定拥有兼容的接口。需要同时使用多个现有子类但为每个子类都创建子类去适配接口不切实际——此时对象适配器可以直接适配父类接口从而惠及所有子类。使用第三方库的应用绝大多数应用在应用层与第三方库之间架设适配器作为中间层使应用与具体库解耦。日后若更换库只需为新产品编写新适配器无需改动应用代码。类适配器与对象适配器的权衡取舍适配器模式并非没有代价选择类适配器还是对象适配器需要结合具体场景权衡本仓库示例采用对象适配器类适配器Class Adapter的特点绑定具体类通过继承与某个具体被适配类绑定因此无法同时适配一个类及其全部子类可覆盖行为由于适配器是被适配类的子类可以重写override被适配类的部分行为单一对象只引入一个对象无需额外的指针间接层去引用被适配者。对象适配器Object Adapter的特点适配面更广单个适配器可以同时与多个被适配类协作即可以适配被适配类及其所有子类并能同时为它们统一添加功能覆盖行为更难想修改被适配类行为时必须为其创建子类再让适配器引用该子类而非原类改造成本更高。JDK 中的适配器实例真实世界的应用证据适配器模式并非纸上谈兵JDK 标准库中就有大量应用例如java.util.Arrays#asList()把数组「适配」为List接口java.util.Collections#list()把Enumeration适配为Listjava.util.Collections#enumeration()把Collection适配为Enumerationjavax.xml.bind.annotation.adapters.XmlAdapter在 Java 对象与 XML 表示之间进行适配转换。这些 API 的共同特征与本仓库示例一致在不修改两端源码的前提下通过一个中间适配层让两个不同接口协同工作。总结适配器模式是解决「接口不兼容」问题最直接、最优雅的结构型模式。通过 java-design-patterns 仓库的 adapter 模块我们可以看到一套教科书级的完整落地目标接口RowingBoat、被适配者FishingBoat、对象适配器FishingBoatAdapter与客户端Captain各司其职配合 AdapterPatternTest 的调用转发验证形成「实现 测试」的闭环。当你在遗留系统集成、第三方库隔离或新旧接口过渡中遇到不兼容问题时不妨像这位船长一样——不重写、不改动只加一个适配器即可让旧船驶向新海。赞分享示例工程教程【免费下载链接】java-design-patternsDesign patterns implemented in Java项目地址https://gitcode.com/GitHub_Trending/ja/java-design-patterns点击查看免费下载相关推荐Java 适配器模式Adapter Pattern实战指南基于 java-design-patterns 仓库实现不兼容接口的无缝协作Java 适配器模式Adapter Pattern实战指南基于 java design patterns 仓库实现不兼容接口的无缝协作 适配器模式Ada示例工程教程Java 适配器模式Adapter Pattern深入解析java-design-patterns 项目 RowingBoat 与 FishingBoat 适配实战Java 适配器模式Adapter Pattern深入解析java design patterns 项目 RowingBoat 与 FishingBoat示例工程教程Java 桥接模式Bridge Pattern完全指南解耦抽象与实现基于 java-design-patterns 仓库实战解析Java 桥接模式Bridge Pattern完全指南解耦抽象与实现基于 java design patterns 仓库实战解析 本文基于 java d示例工程教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联
返回资讯列表 →