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 的見解幫助我們更新了文章。