PHP 中的设计模式介绍(并在 Drupal 中利用它)
已发表: 2022-07-05聪明的人曾经说过 -好的编码器代码,很好的重用。
开发人员经常发现自己反复解决相同类型的问题。 重用代码确实是软件开发的圣杯。 此外,谁不喜欢阅读(和编写)结构良好的代码? Enter - PHP 中的设计模式。
PHP 设计模式已被证明对开发人员非常有用,并且是一个巨大的问题解决者。 遵循最佳实践对于编写高效的代码至关重要。 PHP 设计模式是一个面向对象的编程 (OOP) 概念,现在也用于 Drupal 9 项目。 随着 Drupal 从版本 8 开始采用现代 PHP 和 OOP 概念,可以利用设计模式进行更简洁、更健壮的编程。 在本文中,我们将讨论一些 PHP 中常用的设计模式以及如何在 Drupal 中使用依赖注入等模式。
还在 Drupal 7 上吗? 阅读这篇文章,找到一个方便的清单,帮助您为 Drupal 9 迁移做准备。

PHP中的设计模式是什么?
在软件工程中,设计模式是针对软件设计中常见问题的通用可重复解决方案。 好的面向对象的设计应该是可重用、可维护和可扩展的,PHP 中的设计模式在这方面可能非常有帮助。 它不仅有助于解决问题,还意味着解决共同挑战的最佳方式。
为什么使用 PHP 设计模式
在 PHP 中实现设计模式的一些最显着的好处是:
- PHP 设计模式有助于解决开发过程中面临的重复性问题
- 在 PHP 中使用设计模式使设计人员和开发人员之间的沟通更加高效
- 您可以确信其他开发人员会理解您的代码,因为它遵循设计模式
- 遵循最佳实践有助于构建更强大的应用程序
- 它有助于使开发更快、更容易
PHP中广泛使用的设计模式
设计模式可以在各种情况下用于解决类似的问题。 有超过 30 种设计模式可以大致分为三种类型 - 创建模式、结构模式和行为模式。
创建模式:在对象创建机制中使用的设计模式,用于创建可以与实现它们的系统分离的对象。
结构模式:通过识别实现实体之间关系的简单方法来简化设计
行为模式:它们用于管理对象之间的关系、职责和算法
工厂模式
工厂模式用于构建对象。 没错——构建一个对象而不是创建一个对象。 当我们构建对象时,我们首先创建它,然后初始化它。 通常,它需要应用一定的逻辑并执行多个步骤。 这样一来,将所有内容放在一个地方并在需要以相同方式构建新对象时重新使用它是有意义的。 从根本上说,这就是工厂模式的使用。
为我们的工厂提供一个接口并让我们的代码依赖于它而不是具体工厂是一个好主意。
interface FamilyFactoryInterface { public function create() : Family }
接下来,使用以下类实现工厂接口:
class FamilyFactory implements FamilyFactoryInterface { public function create() : Family { $family = new Family(); // initialize your family return $family; } }
适配器模式
在适配器设计模式中,一个类将一个类的接口转换为另一个类。 在这个例子中,我们有一个TextBook类,它有一个 getTitle() 和 getAuthor() 方法。 客户端需要一个 getTitleAndAuthor() 方法。 为了为demoAdapter “适配” SimpleBook ,我们有一个适配器类BookAdapter ,它接收 TextBook 的一个实例,并在其自己的 getTitleAndAuthor 方法中使用 TextBook getTitle() 和 getAuthor() 方法。
<?php class TextBook { private $title; private $author; function __construct($title_in, $author_in) { $this->title = $title_in; $this->author = $author_in; } function getTitle() { return $this->title; } function getAuthor() { return $this->author; } } class BookAdapter { private $book; function __construct(TextBook $book_in) { $this->book = $book_in; } function getTitleAndAuthors() { return $this->book->getTitle().' by '.$this->book->getAuthor(); } } // client writeln('BEGIN TESTING ADAPTER PATTERN'); writeln(''); $book = new TextBook("Gamma, Helm, Johnson, and Vlissides", "Design Patterns"); $bookAdapter = new BookAdapter($book); writeln('Author and Title: '.$bookAdapter->getTitleAndAuthor()); writeln(''); writeln('END TESTING ADAPTER PATTERN'); function writeln($line_in) { echo $line_in."<br/>"; } ?>
PHP 单例模式
为了将类的实例化限制为单个对象,使用 PHP 中的单例模式。 当整个系统只需要一个对象时,这可能很有用。 在设计 Web 应用程序时,只允许访问某个类的一个实例是有意义的。 为了防止从 Singleton 模式类显式创建对象,使用了私有构造函数。
<?php class Singleton { public static function getInstance() { static $instance = null; if (null === $instance) { $instance = new static(); } return $instance; } protected function __construct() { } private function __clone() { } private function __wakeup() { } } class SingletonChild extends Singleton { } $obj = Singleton::getInstance(); var_dump($obj === Singleton::getInstance()); $obj2 = SingletonChild::getInstance(); var_dump($obj2 === Singleton::getInstance()); var_dump($obj2 === SingletonChild::getInstance()); ?>
PHP中的观察者模式
PHP 观察者模式用于提醒系统的其余部分有关特定位置的特定事件。
例如,如果我们需要创建一个剧院来向评论家展示电影。 我们用当前方法定义类Theatre 。 在放映电影之前,我们想向影评人的手机发送信息。 然后,在电影中间我们要让电影停片 5 分钟,让影评人有一个间隔。 最后,在电影结束后,我们想请评论家留下他们的回应。 因此,在 PHP 的观察者模式中,观察者对象仅在状态更改时才会收到通知。

这就是代码的样子——
class Theater { public function current(Movie $movie) : void { $critics = $movie->getCritics(); $this->message->send($critics, '...'); $movie->play(); $movie->pause(5); $this->progress->interval($critics) $movie->end(); $this->response->request($critics); } }
PHP 的装饰器模式
当您想在运行时更改对象的字符时,可以使用装饰器模式,从而减少不必要的继承和类的数量。 嗯,可以用例子来解释。 假设我们有 Sofa 和 Bed 类,它们都实现了 SleeperInterface。
interface SleeprInterface { public function sleep() : void; } class Sofa implements SleeperInterface { public function sleep() : void { // sleeps on sofa } } class Bed implements SleeperInterface { public function sleep() : void { // sleeps on bed } }
沙发和床都具有相同的睡眠行为。 现在,我们需要其他具有附加功能的沙发和床,当他们睡在沙发或床上时,它们会告诉用户睡眠跟踪。 通过继承,我们可以像这样解决这个问题:
class SmartSofa extends Sofa { public function sleep() : void { parent::sleep(); $this->sleepHours(); } } class SmartBed extends Window { public function sleep() : void { parent::sleep(); $this->sleepHours(); } }
现在我们总共有 4 个班级。 但是,我们只能使用装饰器模式通过 3 个类来解决这个问题。 就是这样:
class SmartSleeper implements SleeperInterface { private $sleeper; public function __construct(SleeperInterface $sleeper) { $this->sleeper = $sleeper; } public function sleep() : void { $this->sleeper->sleep(); $this->sleepHours(); } } $sofa = new Sofa(); $bed = new Bed(); $smartSofa = new SmartSleeper($sofa); $smartBed = new SmartSleeper($bed);
在这里,我们介绍了一种新型的 sleeper,它的作用类似于代理,但在其之上具有额外的功能。
在 Drupal 9 中利用设计模式
虽然在 Drupal 9 之前已经在 Drupal 中建立了许多设计模式,但现在还有更多以前不可用的模式。 其中一些新模式完全取代了旧模式,而另一些则为 Drupal 9 引入了一些新特性。
Drupal 9 中使用的设计模式包括:
- 面向对象编程模式 (OOP)
- 依赖注入
- 工厂模式
- 单例模式
OOP 并不是真正的单一模式,而是一种完全超越设计模式的概念化和结构化代码的彻底方法。 它是当今使用的许多流行软件设计模式的基础,包括在 Drupal 9 中使用的那些。它是在 Drupal 7 中引入的,但没有被广泛使用,也不是必需的。 Drupal 9 中的情况现在有所不同,它被广泛使用,并且是必需的。
依赖注入
依赖注入是一种软件设计模式,它允许您删除硬编码的依赖关系,并且还可以在运行时或编译时更改它们。 添加依赖注入很容易,并且不会干扰您现有的代码。 Drupal 8 引入了服务的概念,以解耦可重用的功能。 core.services.yml是 Drupal 9 中依赖注入的一个例子。我们之前已经讨论过 PHP 中的工厂模式和单例模式。
目前,在 Drupal 中,依赖注入是访问和使用服务的首选方法,应尽可能使用。 与其调用全局服务容器,不如将服务作为参数传递给构造函数或通过 setter 方法注入。 显式传入对象所依赖的服务称为依赖注入。 在某些情况下,依赖项在类构造函数中显式传递。
查看此页面以查找 Drupal 核心中的所有可用服务。 您可以在 Drupal 文档中阅读有关服务的更多信息。
让我们以“entity_type.manager”服务为例,获取 ID=1 的节点的标题。 为了将其注入到我们的自定义服务中,我们只需要获取服务名称并将其作为参数传递到my_module_name.services.yml文件中,如下所示:
my_module_name.services.yml
services: my_module_name.helper: class: Drupal\my_module_name\MyModuleHelper arguments: ['@entity_type.manager']
然后在我们的服务类中,我们只需要在__construct方法中获取服务并将其存储在一个变量中,如下所示:
MyModuleHelper.php
<?php namespace Drupal\my_module_name; use Drupal\Core\Entity\EntityTypeManagerInterface; /** * MyModuleHelper is a simple example of a Drupal 9 service. */ class MyModuleHelper { /** * The entity type manager. * * @var \Drupal\Core\Entity\EntityTypeManagerInterface */ protected $entityTypeManager; /** * Part of the DependencyInjection magic happening here. * * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager * The entity type manager. */ public function __construct(EntityTypeManagerInterface $entity_type_manager) { $this->entityTypeManager = $entity_type_manager; } /** * Returns a title for node_id = 1. */ public function getFirstNodeTitle() { $node = $this->entityTypeManager->getStorage('node')->load(1); return $node->getTitle(); } }
然后我们可以使用实体类型管理器服务并在 getFirstNodeTitle 方法中获取 nid=1 的节点的标题。
非常感谢 Ankitha Shetty 的见解帮助我们更新了文章。