PHPのデザインパターンの紹介(そしてDrupalでそれを活用する)

公開: 2022-07-05

賢い人はかつて言った-良いコーダーコード、素晴らしい再利用。
開発者は、同じタイプの問題を繰り返し解決していることに気付くことがよくあります。 コードの再利用は、ソフトウェア開発の真の聖杯です。 また、適切に構造化されたコードを読み取る(および書き込む)のが嫌いな人はいますか? 入力-PHPのデザインパターン。

PHPデザインパターンは、開発者にとって非常に有用であることが証明されており、大きな問題解決者です。 効率的なコードを作成するには、ベストプラクティスに従うことが重要です。 PHPデザインパターンはオブジェクト指向プログラミング(OOP)の概念であり、現在Drupal9プロジェクトでも使用されています。 バージョン8以降のDrupalによる最新のPHPおよびOOPの概念の採用により、デザインパターンを活用して、よりクリーンで堅牢なプログラミングを行うことができます。 この記事では、PHPで一般的に使用されるいくつかのデザインパターンと、Drupalでの依存性注入などのパターンの使用方法について説明します。

まだDrupal7を使用していますか? この記事を読んで、Drupal9の移行の準備に役立つ便利なチェックリストを見つけてください。

PHPのデザインパターン

PHPのデザインパターンとは何ですか?

ソフトウェアエンジニアリングでは、デザインパターンは、ソフトウェアデザインで一般的に発生する問題に対する一般的な再現可能なソリューションです。 優れたオブジェクト指向デザインは、再利用可能、保守可能、拡張可能である必要があり、PHPのデザインパターンはそれを行うのに非常に役立つ可能性があります。 これは、問題の解決に役立つだけでなく、一般的な課題に対処するための最適な方法を意味します。

PHPデザインパターンを使用する理由

PHPでデザインパターンを実装することの最も重要な利点のいくつかは次のとおりです。

  • PHPデザインパターンは、開発中に直面する繰り返しの問題を解決するのに役立ちます
  • PHPでデザインパターンを使用すると、デザイナーと開発者の間のコミュニケーションがより効率的になります
  • コードはデザインパターンに従っているため、他の開発者があなたのコードを理解できると確信できます。
  • ベストプラクティスに従うことで、より堅牢なアプリケーションを構築できます
  • 開発をより速く、より簡単にするのに役立ちます

PHPで広く使用されているデザインパターン

デザインパターンは、同様の問題を解決するためにさまざまな状況で使用できます。 創造的パターン、構造的パターン、行動的パターンの3つのタイプに大まかに分類できる30を超えるデザインパターンがあります。

作成パターン:オブジェクト作成メカニズムで使用されるデザインパターン。オブジェクトを実装したシステムから切り離すことができるオブジェクトを作成します。

構造パターン:これにより、エンティティ間の関係を実現する簡単な方法を特定することで、設計が容易になります。

行動パターン:オブジェクト間の関係、責任、およびアルゴリズムを管理するために使用されます

ファクトリパターン

ファクトリパターンは、オブジェクトを構築するために使用されます。 そうです—オブジェクトを作成するのではなく、オブジェクトを作成します。 オブジェクトを作成するときは、最初にオブジェクトを作成してから初期化します。 通常、特定のロジックを適用し、複数の手順を実行する必要があります。 それで、それらすべてを1つの場所に置き、同じ方法で新しいオブジェクトを作成する必要があるときはいつでもそれを再利用することは理にかなっています。 基本的に、それはファクトリパターンの使用です。
私たちのファクトリのインターフェイスを用意し、コードを具体的なファクトリではなく、それに依存させることをお勧めします。

 interface FamilyFactoryInterface { public function create() : Family }

次に、次のクラスでファクトリインターフェイスを実装します。

 class FamilyFactory implements FamilyFactoryInterface { public function create() : Family { $family = new Family(); // initialize your family return $family; } }

アダプターパターン

アダプタデザインパターンでは、クラスが1つのクラスのインターフェイスを別のクラスに変換します。 この例では、getTitle()メソッドとgetAuthor()メソッドを持つTextBookクラスがあります。 クライアントはgetTitleAndAuthor()メソッドを期待しています。 SimpleBookdemoAdapterに「適応」させるために、TextBookのインスタンスを取り込んでTextBookのgetTitle()メソッドとgetAuthor()メソッドを独自のgetTitleAndAuthorメソッドで使用するアダプタークラスBookAdapterがあります。

 <?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のシングルトンパターンが使用されます。 これは、システム全体で1つのオブジェクトのみが必要な場合に役立ちます。 Webアプリケーションの設計中に、特定のクラスの1つのインスタンスにのみアクセスを許可することは理にかなっています。 シングルトンパターンクラスからのオブジェクトの明示的な作成を防ぐために、プライベートコンストラクタが利用されます。

 <?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 Observerパターンは、特定の場所での特定のイベントについてシステムの残りの部分に警告するために使用されます。
たとえば、批評家に映画を上映するための劇場を作成する必要がある場合です。 現在のメソッドでクラスTheaterを定義します。 映画を上映する前に、批評家の携帯電話にメッセージを送りたいと思います。 次に、映画の途中で、批評家に間隔を空けるために、映画を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);

ここでは、プロキシのように機能するが、その上に追加機能を備えた新しいタイプのスリーパーを紹介しました。

Drupal9でのデザインパターンの活用

Drupal 9より前にDrupal内ですでに確立されている多くのデザインパターンがありますが、以前は利用できなかったパターンがさらに多くなりました。 これらの新しいパターンのいくつかは古いパターンを完全に置き換えますが、他のパターンはDrupal9にいくつかの新機能を導入します。
Drupal9で使用されるデザインパターンは次のとおりです。

  • オブジェクト指向プログラミングパターン(OOP)
  • 依存性注入
  • ファクトリパターン
  • シングルトンパターン

OOPは実際には単一のパターンではありませんが、単なるデザインパターンを超えた、コードの概念化と構造化の完全に根本的な方法です。 これは、Drupal 9で使用されているものを含め、現在使用されている多くの一般的なソフトウェアデザインパターンの基礎です。Drupal7で導入されましたが、広く使用されておらず、必須ではありませんでした。 Drupal 9の状況は現在異なり、広く使用されており、必須です。

依存性注入

依存性注入は、ハードコードされた依存関係を削除し、実行時またはコンパイル時に変更できるようにするソフトウェアデザインパターンです。 依存性注入の追加は簡単で、既存のコードに干渉することはありません。 Drupal 8は、再利用可能な機能を分離するためにサービスの概念を導入しました。 core.services.ymlは、Drupal 9での依存性注入の例です。PHPでのファクトリパターンとシングルトンパターンについては、すでに説明しました。

現在、Drupalでは、依存性注入がサービスにアクセスして使用するための推奨される方法であり、可能な限り使用する必要があります。 グローバルサービスコンテナを呼び出す代わりに、サービスはコンストラクターに引数として渡されるか、セッターメソッドを介して注入されます。 オブジェクトが依存するサービスを明示的に渡すことを、依存性注入と呼びます。 場合によっては、依存関係はクラスコンストラクターで明示的に渡されます。

Drupalコアで利用可能なすべてのサービスを見つけるには、このページをチェックしてください。 サービスの詳細については、Drupalのドキュメントをご覧ください。

ID = 1のノードのタイトルを取得する例として、「entity_type.manager」サービスを考えてみましょう。 カスタムサービスに挿入するには、サービス名を取得して、以下に示すように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のノードのタイトルを取得できます。

記事の更新に役立った洞察を提供してくれたAnkithaShettyに大いに感謝します。