
示例工程教程【免费下载链接】DesignPatternsPHPSample code for several design patterns in PHP 8.x项目地址https://gitcode.com/gh_mirrors/de/DesignPatternsPHP点击查看免费下载本指南以仓库Structural/目录为核心系统讲解结构型设计模式Structural Design Patterns在 PHP 8.x 中的完整落地从模式的定义与适用场景到每个模式在仓库中的真实类结构、关键源码与配套单元测试。读完本文你将掌握 Adapter、Bridge、Composite、DataMapper、Decorator、DependencyInjection、Facade、FluentInterface、Flyweight、Proxy、Registry 共 11 种模式的本仓库实现细节并能在自己的 PHP 项目中按需选用与验证。一、结构型设计模式定义与仓库组织仓库根目录的 Structural/README.rst 开篇即给出核心定义In Software Engineering, Structural Design Patterns are Design Patterns that ease the design by identifying a simple way to realize relationships between entities.即结构型设计模式关注的是如何以简单的方式实现实体类/对象之间的关系。与创建型模式关注对象如何产生和行为型模式关注对象间如何交互不同结构型模式回答的是这些类以什么结构组合起来最合理、最解耦。仓库Structural/目录下按模式逐一组织共包含 11 个实现每个子目录都遵循统一布局模式源码 PHP 文件、uml/uml.png类图、README.rst说明文档、Tests/单元测试Adapter适配器 / 包装器Bridge桥接Composite组合DataMapper数据映射器Decorator装饰器DependencyInjection依赖注入Facade门面FluentInterface流式接口Flyweight享元Proxy代理Registry注册表下文逐一展开每个模式的目的Purpose、适用场景示例Examples、源码实现要点与测试验证所有实现均基于 PHP 8.x 语法构造器属性提升、只读/私有属性、联合类型等。二、Adapter / Wrapper让不兼容的接口协同工作目的将一个类的接口翻译为客户端所期望的兼容接口。Adapter 通过向客户端提供自己的接口、同时内部使用原类的接口使得原本因接口不兼容而无法协作的类可以一起工作。典型场景摘自 Adapter/README.rst数据库客户端库适配器统一不同数据库驱动的差异接口对接多个不同 Web 服务时用适配器把数据归一化使各服务的输出格式一致。仓库实现目录包含 Book.php、PaperBook.php纸质书实现Book接口、EBook.php电子书接口与纸质书接口不兼容、Kindle.phpKindle 电子书实现以及桥接两者的 EBookAdapter.php。EBookAdapter同时实现Book接口并持有EBook实例把翻页等纸质书语义翻译为电子书接口的按压前进/后退操作——这正是提供自己的接口、使用原接口的典型结构。配套测试 Tests/AdapterTest.php 验证了通过适配器调用Book接口方法时行为正确透传到Kindle。三、Bridge抽象与实现解耦各自独立变化目的Bridge/README.rst将抽象从其实现中解耦使两者可以独立变化。桥接模式把做什么抽象层与怎么做实现层分成两个独立的类层次通过组合关系连接。仓库实现Formatter.php 定义格式化实现接口PlainTextFormatter.php 与 HtmlFormatter.php 是两个可互换的实现Service.php 是抽象基类持有Formatter实现HelloWorldService.php 与 PingService.php 是具体服务。运行时将任意Formatter注入任意Service即可在不改动服务端的前提下切换输出格式Tests/BridgeTest.php 对此进行了断言。这正是抽象与实现各自扩展、互相组合的桥接结构。四、Composite像对待单个对象一样对待对象组目的Composite/README.rst将一组对象当作单个对象实例来对待。组合模式让叶子节点与容器节点实现同一接口客户端无需区分一个元素还是一组元素。典型场景原文示例一个表单类实例把它的所有表单元素当作表单的单个实例来处理——当调用render()时它依次遍历所有子元素并逐一调用子元素的render()。仓库实现Renderable.php 定义统一渲染接口InputElement.php 与 TextElement.php 是叶子元素Form.php 是容器内部持有Renderable[]元素数组并在render()中递归调用每个子元素的render()从而形成树形结构。客户端只需面向Renderable编程即可渲染整棵表单树Tests/CompositeTest.php 验证了容器与叶子行为的一致性。五、DataMapper让领域层与持久层彼此独立目的DataMapper/README.rstDataMapper 是一种数据访问层在持久化数据存储通常是关系数据库与内存数据表示领域层之间执行双向数据传输。该模式的目标是让内存表示与持久化存储互相独立也独立于 Mapper 本身。数据访问层由一个或多个 MapperData Access Object组成通用 Mapper 处理多种领域实体类型专用 Mapper 只处理一种或少数几种。关键区别原文强调与 Active Record 模式不同DataMapper 模式下数据模型遵循单一职责原则——领域对象只承载业务数据不负责自身持久化。典型场景Doctrine2 等 ORM 中名为 EntityRepository 的 DAO 即属于此类。仓库实现User.php 是纯领域实体无持久化逻辑StorageAdapter.php 提供底层存储读写能力UserMapper.php 负责在两者之间转换数据。Tests/DataMapperTest.php 验证了从存储读取 → 映射为领域对象 → 修改 → 写回存储的完整闭环。六、Decorator动态为类实例添加新功能目的Decorator/README.rst动态地为类实例添加新功能而不是通过继承在编译期固化行为。装饰器与被装饰对象实现同一接口并在内部转发调用、附加职责。典型场景原文示例Web 服务层中为 REST 服务装饰 JSON 或 XML 输出同一场景下通常只允许启用其一。仓库实现以酒店预订为例——Booking.php 定义预订接口DoubleRoomBooking.php 是基础实现BookingDecorator.php 是抽象装饰器从源码可见其核心abstract class BookingDecorator implements Booking { public function __construct(protected Booking $booking) { } }它通过构造器属性提升持有一个Booking实例并实现同一接口ExtraBed.php加床与 WiFi.phpWiFi 增值继承装饰器在getPrice()/getDescription()中叠加自己的价格与描述后转发给被装饰对象。多个装饰器可以任意嵌套组合如双人间 WiFi 加床Tests/DecoratorTest.php 对叠加后的价格与描述进行了断言。七、DependencyInjection松耦合、可测试、可扩展目的DependencyInjection/README.rst实现松散耦合的架构从而获得更好的可测试性、可维护性与可扩展性。用法说明原文DatabaseConfiguration被注入进来DatabaseConnection从$config获取它所需的一切。如果没有 DI配置会在DatabaseConnection内部直接创建这对测试和扩展都很不利。典型场景Doctrine2 ORM 将配置注入Connection对象测试时可轻松创建配置的 mock 并注入许多框架提供 DI 容器通过配置数组创建对象并注入需要之处如 Controller。仓库实现DatabaseConfiguration.php 持有连接参数可从源码构造器参数确认 host、port、username、password 等配置项DatabaseConnection.php 通过构造器接收DatabaseConfiguration而非自行 new。Tests/DependencyInjectionTest.php 演示了构造注入的用法——这正是本仓库推荐替代 Registry 全局状态的正统方案。八、Facade降低耦合、遵循迪米特法则目的Facade/README.rstFacade 的首要目标不是帮你省去阅读复杂 API 手册那只是副作用而是降低客户端与子系统之间的耦合并遵循迪米特法则Law of Demeter。Facade 通过内嵌多个有时只有一个接口把客户端与子系统解耦同时降低复杂度。设计要点原文强调Facade不禁止你访问子系统一个子系统可以有多个Facade好的 Facade 内部没有new如果每个方法里都有多次对象创建那它不是 Facade而是 Builder 或 [Abstract|Static|Simple] Factory [Method]最佳 Facade内部无new且构造函数参数为接口类型提示若确需创建新实例应传入 Factory 作为参数。仓库实现Bios.php 与 OperatingSystem.php 是子系统接口Facade.php 是其门面。从源码可见其完全符合上述准则class Facade { public function __construct(private Bios $bios, private OperatingSystem $os) { } public function turnOn() { $this-bios-execute(); $this-bios-waitForKeyPress(); $this-bios-launch($this-os); } public function turnOff() { $this-os-halt(); $this-bios-powerDown(); } }构造器仅注入接口类型方法内没有任何new客户端只需调用turnOn()/turnOff()即可完成开机/关机流程。Tests/FacadeTest.php 验证了调用门面后子系统方法的实际执行顺序。九、FluentInterface代码像自然语言句子一样易读目的FluentInterface/README.rst写出像英语等自然语言句子一样易于阅读的代码。实现方式通常是让每个修改方法返回$this从而支持方法链式调用。典型场景原文示例Doctrine2 的 QueryBuilder 采用类似示例类的链式写法PHPUnit 使用流式接口构建 mock 对象。仓库实现Sql.php 提供一个 SQL 构建器示例其链式方法可从测试推断如select、from、where等每次调用后返回自身使查询语句的拼装顺序读起来接近自然语言。Tests/FluentInterfaceTest.php 验证了链式调用的最终结果。流式接口通常与其他模式如 Builder结合使用是提升 API 可读性的常用手法。十、Flyweight共享相似对象最小化内存占用目的Flyweight/README.rst尽可能与相似对象共享内存从而最小化内存使用。当应用中存在大量状态差异很小的对象时使用常见做法是把状态保存在外部数据结构中在需要时传给享元对象。仓库实现Text.php 定义文本接口Character.php 表示单字符Word.php 表示单词二者是享元对象TextFactory.php 是享元工厂从源码可见其缓存与复用机制class TextFactory implements Countable { private array $charPool []; public function get(string $name): Text { if (!isset($this-charPool[$name])) { $this-charPool[$name] $this-create($name); } return $this-charPool[$name]; } private function create(string $name): Text { if (strlen($name) 1) { return new Character($name); } return new Word($name); } public function count(): int { return count($this-charPool); } }工厂按名称缓存已创建对象相同内容如重复出现的单词只实例化一次create()依据字符串长度决定生成Character还是Wordcount()反映池中唯一对象数量。Tests/FlyweightTest.php 验证了相同文本返回同一实例、池大小受限等核心行为。十一、Proxy为昂贵或难以复制的对象提供接口目的Proxy/README.rst为任何昂贵或难以复制的对象提供接口。代理对象与真实对象实现同一接口客户端无感知地通过代理访问真实对象代理可在访问前附加控制或延迟初始化。典型场景原文示例Doctrine2 使用代理实现框架魔法如懒加载 lazy initialization用户仍然使用自己的实体类从不直接接触代理。仓库实现BankAccount.php 是账户接口HeavyBankAccount.php 是昂贵的真实实现余额计算开销大BankAccountProxy.php 是代理。从源码可见懒加载与结果缓存的关键class BankAccountProxy extends HeavyBankAccount implements BankAccount { private ?int $balance null; public function getBalance(): int { if ($this-balance null) { $this-balance parent::getBalance(); } return $this-balance; } }代理继承真实实现并实现同一接口把昂贵的getBalance()计算延迟到真正需要时才执行且对同一实例只计算一次结果缓存于$balance。Tests/ProxyTest.php 验证了通过代理访问的余额结果与延迟计算的特性。十二、Registry集中存储常用对象慎用目的Registry/README.rst为应用中经常使用的对象提供集中存储。通常用只含静态方法的抽象类实现或结合 Singleton 模式。文档同时给出强烈警示这引入了全局状态global state应尽量避免应改用依赖注入Dependency Injection实现。该文档的参考链接指向 Service Locator 模式说明 Registry 与 Service Locator 属于同一类服务定位思路。仓库实现Service.php 是服务接口Registry.php 是抽象注册表。从源码可见其全局状态与白名单机制abstract class Registry { public const LOGGER logger; private static array $services []; private static array $allowedKeys [self::LOGGER]; final public static function set(string $key, Service $value) { if (!in_array($key, self::$allowedKeys)) { throw new InvalidArgumentException(Invalid key given); } self::$services[$key] $value; } final public static function get(string $key): Service { if (!in_array($key, self::$allowedKeys) || !isset(self::$services[$key])) { throw new InvalidArgumentException(Invalid key given); } return self::$services[$key]; } }代码注释中明确写道这会在应用中引入无法在测试中 mock 的全局状态因此被视为反模式anti-pattern建议改用依赖注入。set()/get()均通过$allowedKeys白名单校验键名非法键抛出InvalidArgumentException。Tests/RegistryTest.php 覆盖了正常存取与非法键报错两种路径。本模式在仓库中的定位是反例示范 替代方案提醒理解它但生产代码优先选择依赖注入。十三、运行与验证用单元测试印证每个模式仓库为每个模式都配了独立测试全部位于各模式的Tests/目录例如 Structural/Adapter/Tests/AdapterTest.php、Structural/Facade/Tests/FacadeTest.php。仓库根目录提供 composer.json 与 phpunit.xml.distPHPUnit 配置以及 psalm.xml静态分析、phpcs.xml.dist代码规范。在安装依赖后可基于这些配置运行对应测试文件来验证各模式的真实行为若需在 CI 或本地一键执行全部测试可参照 Makefile 中定义的命令。所有源码统一使用declare(strict_types1);与DesignPatterns\Structural\模式名命名空间如DesignPatterns\Structural\Decorator体现了 PHP 8.x 下类型安全与 PSR 风格的最佳实践。十四、选型小结模式解决的核心问题本仓库代表实现Adapter接口不兼容时的翻译桥EBookAdapter.phpBridge抽象与实现解耦、可独立变化Service.php Formatter.phpComposite单个对象与对象组统一处理Form.phpDataMapper领域层与持久层双向转换、互不依赖UserMapper.phpDecorator运行时动态叠加功能BookingDecorator.phpDependencyInjection构造注入实现松耦合DatabaseConnection.phpFacade降低客户端与子系统的耦合无newFacade.phpFluentInterface链式调用让代码如自然语言Sql.phpFlyweight共享对象、节省内存TextFactory.phpProxy为昂贵对象提供延迟访问接口BankAccountProxy.phpRegistry集中存储常用对象引入全局状态慎用Registry.php选用建议综合各模式文档观点对象创建昂贵或难复制 → Proxy/Flyweight接口不匹配需要桥接 → Adapter/Bridge对象树或动态增强 → Composite/Decorator层与层解耦 → DataMapper/DI/Facade需要全局注册表时优先用依赖注入替代 Registry。深入研读时可先读各子目录的 README.rst 了解意图再对照uml/uml.png类图理解结构最后运行Tests/下的测试验证行为三者结合即可完整掌握这套 PHP 8.x 结构型模式参考实现。赞分享示例工程教程【免费下载链接】DesignPatternsPHPSample code for several design patterns in PHP 8.x项目地址https://gitcode.com/gh_mirrors/de/DesignPatternsPHP点击查看免费下载相关推荐深入解析 DesignPatternsPHP 中的行为型设计模式13 种模式的 PHP 8 实现指南深入解析 DesignPatternsPHP 中的行为型设计模式13 种模式的 PHP 8 实现指南 导读 本文围绕开源仓库 DesignPatternsPH示例工程教程架构解耦实战DesignPatternsPHP结构型设计模式全解析架构解耦实战DesignPatternsPHP结构型设计模式全解析 结构型设计模式是解决软件实体间关系的核心方案本指南基于 DesignPatternsPH示例工程教程Apache Ossie指标语义工作组深度解析grain、过滤与派生指标的未来Apache Ossie指标语义工作组深度解析grain、过滤与派生指标的未来 Apache Ossie孵化中是一个厂商中立的开源语义模型交换标准目标是示例工程教程上一篇番茄小说下载器5分钟打造您的个人数字图书馆下一篇Forza-Mods-AIO极限竞速地平线游戏修改的终极指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考