DesignPatternsPHP 适配器模式(Adapter Pattern)实战:用 PHP 8 让接口不兼容的类协同工作 示例工程教程【免费下载链接】DesignPatternsPHPSample code for several design patterns in PHP 8.x项目地址https://gitcode.com/gh_mirrors/de/DesignPatternsPHP点击查看免费下载导读本文以 DesignPatternsPHP 仓库中 Structural/Adapter 模块为蓝本深入讲解适配器模式Adapter / Wrapper的核心意图、角色划分与 PHP 8 实现细节。你将掌握如何通过EBookAdapter这样的适配器类把接口不兼容的第三方组件如电子书Kindle无缝接入客户端期望的Book接口并看懂配套单元测试的验证方式从而在真实项目中低成本完成异构接口的整合。适配器模式要解决什么问题核心目的接口翻译根据 Structural/Adapter/README.rst 的定义适配器模式又称 Wrapper包装器的目的是将一个类的接口翻译成客户端所期望的兼容接口。适配器通过向客户端提供自身接口、同时在内部使用被适配类的原始接口让那些因接口不兼容而本无法协作的类能够一起工作。一句话概括不改动客户端代码也不改动被适配类只在两者之间插入一层翻译。客户端只认识Book接口被适配对象只有EBook接口适配器同时实现目标接口、持有被适配对象从而把 A 的语言翻译成 B 的语言。典型应用场景原文档给出了两个经典场景数据库客户端库适配器不同数据库厂商MySQL、PostgreSQL、Oracle提供不同的驱动 API通过统一适配层对外暴露一致的数据访问接口多 Web 服务数据归一化同时接入多个不同的 Web 服务用各自的适配器把返回的数据规范化让下游拿到格式一致的结果。这两个场景的共同点都是异构接口收敛为统一契约这正是适配器模式价值最大的地方。模式结构与角色分析适配器模式通常包含四个参与者本仓库的实现一一对应角色对应类/接口文件路径职责目标接口TargetBookStructural/Adapter/Book.php客户端期望的统一契约open()、turnPage()、getPage()适配者AdapteeKindleStructural/Adapter/Kindle.php接口不兼容的既有类拥有自己的一套命名与行为适配器AdapterEBookAdapterStructural/Adapter/EBookAdapter.php实现目标接口内部将调用翻译给适配者客户端可用的现有实现PaperBookStructural/Adapter/PaperBook.php直接实现Book接口的参照实现用于对照从 UML 类图Structural/Adapter/uml/uml.png可以直观看到PaperBook与EBookAdapter都通过虚线箭头实现Book接口而Kindle并不实现Book它只是作为被适配对象被EBookAdapter持有。该图是典型的对象适配器结构——适配器通过组合持有被适配对象实例而非继承来完成转换。源码逐层拆解从接口到适配器第一步目标接口 BookStructural/Adapter/Book.php 定义了客户端眼中一本书应有的三种能力namespace DesignPatterns\Structural\Adapter; interface Book { public function turnPage(); public function open(); public function getPage(): int; }注意getPage()的返回类型是int——客户端只关心当前读到第几页这一个数字。这是后文适配难点返回值不一致埋下的伏笔。第二步参照实现 PaperBookStructural/Adapter/PaperBook.php 是符合Book契约的标准答案纸质书打开后定位到第 1 页翻页就是页码自增。class PaperBook implements Book { private int $page; public function open(): void { $this-page 1; } public function turnPage(): void { $this-page; } public function getPage(): int { return $this-page; } }它存在的意义在于为客户端提供原本就能正常工作的基准行为后续验证适配器时EBookAdapter的输出必须与PaperBook完全一致。第三步不兼容的适配者 EBook / Kindle电子书的世界观与纸质书完全不同。Structural/Adapter/EBook.php 定义了另一套接口interface EBook { public function unlock(); public function pressNext(); /** * returns current page and total number of pages, like [10, 100] is page 10 of 100 * * return int[] */ public function getPage(): array; }差异一目了然维度BookEBook打开方式open()unlock()解锁设备翻页方式turnPage()pressNext()按下翻页键页码返回int单一当前页int[][当前页, 总页数]二元组而 Structural/Adapter/Kindle.php 正是这个外部适配者的具体实现。源码注释特别强调在生产代码中它可能就是另一个包、第三方厂商的代码命名风格与实现方式都自成一体class Kindle implements EBook { private int $page 1; private int $totalPages 100; public function pressNext() { $this-page; } public function unlock() { } public function getPage(): array { return [$this-page, $this-totalPages]; } }Kindle完全不知道Book接口的存在直接把它交给客户端必然报错——这正是接口不兼容的具体体现。第四步适配器 EBookAdapter —— 模式的灵魂Structural/Adapter/EBookAdapter.php 是核心。源码注释明确指出这是适配器注意它实现了Book因此你不需要修改使用Book的客户端代码。class EBookAdapter implements Book { public function __construct(protected EBook $eBook) { } public function open() { $this-eBook-unlock(); } public function turnPage() { $this-eBook-pressNext(); } public function getPage(): int { return $this-eBook-getPage()[0]; } }三处关键设计值得深读构造注入通过构造函数把EBook这里是Kindle实例注入适配器使用protected属性PHP 8 的构造器属性提升语法体现组合优于继承逐方法翻译open()翻译为unlock()turnPage()翻译为pressNext()客户端调用什么适配器就转发什么返回值整形最巧妙的是getPage()——EBook::getPage()返回[当前页, 总页数]二元组而Book契约只允许返回int因此适配器取[0]元素只把当前页暴露给客户端。源码注释将其称为适配后的行为adapted behavior。客户端自始至终只与Book打交道浑然不知背后操作的是 Kindle 硬件这就是适配器的透明性。单元测试适配正确性的双重验证Structural/Adapter/Tests/AdapterTest.php 用两个用例锁定了行为契约public function testCanTurnPageOnBook() { $book new PaperBook(); $book-open(); $book-turnPage(); $this-assertSame(2, $book-getPage()); } public function testCanTurnPageOnKindleLikeInANormalBook() { $kindle new Kindle(); $book new EBookAdapter($kindle); $book-open(); $book-turnPage(); $this-assertSame(2, $book-getPage()); }第一个用例testCanTurnPageOnBook验证基准纸质书打开即第 1 页翻一页到第 2 页第二个用例testCanTurnPageOnKindleLikeInANormalBook验证适配Kindle经过EBookAdapter包装后可以像普通书一样打开、翻页且页码结果同样为 2。两个用例断言完全一致从测试层面证明了客户端感知不到适配层存在的核心目标。测试目录遵循仓库约定所有模式的测试都放在各模块Tests/目录下并被 phpunit.xml.dist 中的Structural/*/Tests规则自动收集。运行与验证仓库根目录的 composer.json 要求php 8.0项目描述为 PHP 8.x 设计模式示例并在autoload.classmap中声明Behavioral、Creational、Structural、More四个目录因此安装依赖后即可运行测试composer install vendor/bin/phpunit Structural/Adapter/Tests/AdapterTest.php若希望跑全仓库所有设计模式的测试直接执行vendor/bin/phpunit按 phpunit.xml.dist 的配置PHPUnit 会自动扫描四个目录下所有*Test.php文件。适配器模式的最佳实践要点结合源码可以提炼出几条实战准则客户端零改动适配器必须实现目标接口客户端代码保持原样仓库通过EBookAdapter implements Book印证适配者零侵入绝不修改第三方/既有类只在其外层做包装Kindle未做任何改动返回值也要翻译接口不兼容不只体现在方法名还可能体现在返回类型与数据结构getPage()的array转int就是例证对象适配器优先通过构造器组合被适配对象比类适配器继承适配者更灵活也符合组合优于继承的原则用测试固化契约让适配器与现有实现跑同一组断言确保任何重构都不会破坏客户端语义。回到原文档列举的场景无论是对接异构数据库客户端还是归一化多个 Web 服务的数据遵循的都是同一套目标接口 适配器翻译的骨架。掌握 Structural/Adapter 这个最小可运行示例后你就可以把同样的结构迁移到任何需要接口兼容的现实项目中了。赞分享示例工程教程【免费下载链接】DesignPatternsPHPSample code for several design patterns in PHP 8.x项目地址https://gitcode.com/gh_mirrors/de/DesignPatternsPHP点击查看免费下载相关推荐CANN Runtime 错误码 EP0004 全解析Dump 配置文件解析失败的定位与修复指南CANN Runtime 错误码 EP0004 全解析Dump 配置文件解析失败的定位与修复指南 导读 EP0004File Operation Error示例工程教程适配器模式Adapter Pattern实战解析接口不兼容场景下的类复用方案 —— 基于 Unity3DTraining 仓库 C 示例适配器模式Adapter Pattern实战解析接口不兼容场景下的类复用方案 —— 基于 Unity3DTraining 仓库 C 示例 适配器模式是解决示例工程JavaScript适配器模式让不兼容接口完美协作的3个技巧JavaScript适配器模式让不兼容接口完美协作的3个技巧 你是否曾经遇到过这样的情况两个功能强大的组件却因为接口不兼容而无法协同工作 在Java上一篇OptiScaler深度配置指南解决游戏超分辨率替换的三大技术挑战下一篇告别抢票焦虑大麦自动抢票工具让你轻松购票创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考