Eine Einführung in Design Patterns in PHP (und deren Nutzung in Drupal)
Veröffentlicht: 2022-07-05Jemand Schlaues hat einmal gesagt: Guter Programmiercode, großartige Wiederverwendung.
Entwickler finden sich oft dabei wieder, die gleiche Art von Problemen wiederholt zu lösen. Die Wiederverwendung von Code ist wirklich der heilige Gral der Softwareentwicklung. Außerdem, wer liest (und schreibt) nicht gerne gut strukturierten Code? Enter - Entwurfsmuster in PHP.
PHP-Designmuster haben sich als äußerst nützlich für Entwickler erwiesen und sind ein großer Problemlöser. Das Befolgen von Best Practices ist entscheidend für das Schreiben von effizientem Code. PHP Design Patterns ist ein Konzept der objektorientierten Programmierung (OOP), das jetzt auch in Drupal 9-Projekten verwendet wird. Mit Drupals Übernahme moderner PHP- und OOP-Konzepte seit Version 8 können Designmuster für eine sauberere und robustere Programmierung genutzt werden. In diesem Artikel besprechen wir einige häufig verwendete Designmuster in PHP und die Verwendung von Mustern wie Abhängigkeitsinjektionen in Drupal.
Immer noch auf Drupal 7? Lesen Sie diesen Artikel, um eine praktische Checkliste zu finden, die Ihnen bei der Vorbereitung auf eine Drupal 9-Migration helfen wird.
Was sind Designmuster in PHP?
In der Softwareentwicklung ist ein Entwurfsmuster eine allgemeine, wiederholbare Lösung für häufig auftretende Probleme im Softwaredesign. Gute objektorientierte Designs sollten wiederverwendbar, wartbar und erweiterbar sein und Design Patterns in PHP könnten dabei sehr hilfreich sein. Es hilft nicht nur bei der Lösung von Problemen, sondern impliziert einen optimalen Weg zur Bewältigung gemeinsamer Herausforderungen.
Warum PHP-Entwurfsmuster verwenden
Einige der wichtigsten Vorteile der Implementierung von Designmustern in PHP sind:
- PHP-Designmuster helfen bei der Lösung sich wiederholender Probleme, die während der Entwicklung auftreten
- Die Verwendung von Designmustern in PHP macht die Kommunikation zwischen Designern und Entwicklern effizienter
- Sie können sicher sein, dass andere Entwickler Ihren Code verstehen werden, da er Designmustern folgt
- Das Befolgen von Best Practices hilft beim Erstellen robusterer Anwendungen
- Es hilft, die Entwicklung schneller und einfacher zu machen
Weit verbreitete Design Patterns in PHP
Entwurfsmuster können in verschiedenen Situationen verwendet werden, um ähnliche Probleme zu lösen. Es gibt mehr als 30 Designmuster, die grob in drei Typen eingeteilt werden können – Kreations-, Struktur- und Verhaltensmuster.
Erstellungsmuster: Entwurfsmuster, die in Objekterstellungsmechanismen verwendet werden, um Objekte zu erstellen, die von dem System, das sie implementiert hat, entkoppelt werden können.
Strukturelle Muster: Dies erleichtert das Design, indem einfache Wege identifiziert werden, um Beziehungen zwischen Entitäten zu realisieren
Verhaltensmuster: Sie werden verwendet, um Beziehungen, Verantwortlichkeiten und Algorithmen zwischen Objekten zu verwalten
Fabrikmuster
Ein Fabrikmuster wird verwendet, um ein Objekt zu bauen. Das ist richtig – ein Objekt erstellen und kein Objekt erstellen. Wenn wir das Objekt erstellen, erstellen wir es zuerst und initialisieren es dann. Normalerweise ist es erforderlich, eine bestimmte Logik anzuwenden und mehrere Schritte auszuführen. Daher ist es sinnvoll, all das an einem Ort zu haben und es wiederzuverwenden, wenn Sie ein neues Objekt auf die gleiche Weise erstellen müssen. Im Grunde ist das die Verwendung des Fabrikmusters.
Es ist eine großartige Idee, eine Schnittstelle für unsere Fabrik zu haben und unseren Code davon abhängig zu machen und nicht von einer konkreten Fabrik.
interface FamilyFactoryInterface { public function create() : Family }
Als nächstes implementieren Sie die Factory-Schnittstelle mit der folgenden Klasse:
class FamilyFactory implements FamilyFactoryInterface { public function create() : Family { $family = new Family(); // initialize your family return $family; } }
Adaptermuster
In Adapter Design Pattern transformiert eine Klasse die Schnittstelle einer Klasse in eine andere Klasse. In diesem Beispiel haben wir eine TextBook -Klasse mit den Methoden getTitle() und getAuthor(). Der Client erwartet eine Methode getTitleAndAuthor(). Um SimpleBook für demoAdapter "anzupassen", haben wir eine Adapterklasse, BookAdapter , die eine Instanz von TextBook aufnimmt und die getTitle() - und getAuthor() -Methoden von TextBook in ihrer eigenen getTitleAndAuthor -Methode verwendet.
<?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-Singleton-Muster
Um die Instanziierung einer Klasse auf ein einzelnes Objekt zu beschränken, wird in PHP ein Singleton-Pattern verwendet. Dies kann nützlich sein, wenn systemweit nur ein Objekt benötigt wird. Es ist sinnvoll, beim Entwerfen von Webanwendungen den Zugriff nur auf eine Instanz einer bestimmten Klasse zuzulassen. Um die explizite Erstellung von Objekten aus der Singleton-Musterklasse zu verhindern, wird ein privater Konstruktor verwendet.
<?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()); ?>
Beobachtermuster in PHP
Das PHP-Observer-Muster wird verwendet, um den Rest des Systems über bestimmte Ereignisse an bestimmten Stellen zu informieren.
Zum Beispiel, wenn wir ein Theater schaffen müssen, um den Kritikern Filme zu zeigen. Wir definieren die Klasse Theater mit der aktuellen Methode. Vor der Vorstellung des Films wollen wir Nachrichten an die Handys der Kritiker senden. Dann wollen wir mitten im Film den Film für 5 Minuten anhalten, um den Kritikern eine Pause zu gönnen. Schließlich möchten wir nach dem Ende des Films die Kritiker bitten, ihre Antwort zu hinterlassen. Im Beobachtermuster für PHP wird das Beobachterobjekt also nur benachrichtigt, wenn sich der Status ändert.
So sieht Code aus -
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); } }
Decorator-Muster für PHP
Das Decorator-Muster wird verwendet, wenn Sie den Charakter eines Objekts zur Laufzeit ändern und damit unnötige Vererbungen und die Anzahl der Klassen reduzieren möchten. Nun, es kann mit Beispielen erklärt werden. Nehmen wir an, wir haben die Klassen Sofa und Bed, und beide implementieren 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 } }
Sowohl die Sofas als auch die Betten haben das gleiche Schlafverhalten. Jetzt brauchen wir andere Sofas und Betten mit zusätzlichen Funktionen, die den Benutzern das Schlaftracking mitteilen, wenn sie auf den Sofas oder Betten schlafen. Mit Vererbung können wir dieses Problem einfach so lösen:
class SmartSofa extends Sofa { public function sleep() : void { parent::sleep(); $this->sleepHours(); } } class SmartBed extends Window { public function sleep() : void { parent::sleep(); $this->sleepHours(); } }
Jetzt haben wir insgesamt 4 Klassen. Allerdings konnten wir dieses Problem mit 3 Klassen nur mit dem Decorator-Muster lösen. Hier ist wie:
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);
Hier haben wir eine neue Art von Sleeper eingeführt, die wie ein Proxy fungiert, aber darüber hinaus eine zusätzliche Funktionalität bietet.
Nutzung von Designmustern in Drupal 9
Während in Drupal bereits vor Drupal 9 viele Designmuster etabliert wurden, gibt es jetzt viel mehr Muster, die zuvor nicht verfügbar waren. Einige dieser neuen Muster ersetzen ältere vollständig, während andere einige neue Funktionen in Drupal 9 einführen.
Zu den in Drupal 9 verwendeten Designmustern gehören:
- Objektorientiertes Programmiermuster (OOP)
- Abhängigkeitsinjektionen
- Fabrikmuster
- Singleton-Muster
OOP ist nicht wirklich ein einzelnes Muster, sondern eine völlig radikale Art, Code zu konzeptualisieren und zu strukturieren, die weit über reine Entwurfsmuster hinausgeht. Es ist die Grundlage für viele beliebte Software-Designmuster, die heute verwendet werden, einschließlich der in Drupal 9 verwendeten. Es wurde in Drupal 7 eingeführt, aber nicht umfassend verwendet und war nicht erforderlich. Anders sieht es bei Drupal 9 aus, es ist weit verbreitet und wird benötigt.
Abhängigkeitsinjektionen
Abhängigkeitsinjektion ist ein Softwaredesignmuster, mit dem Sie hartcodierte Abhängigkeiten entfernen und es auch ermöglichen würden, sie entweder zur Laufzeit oder zur Kompilierzeit zu ändern. Das Hinzufügen von Abhängigkeitsinjektionen ist einfach und mischt sich nicht in Ihren vorhandenen Code ein. Drupal 8 führte das Konzept der Dienste ein, um die wiederverwendbaren Funktionalitäten zu entkoppeln. core.services.yml ist ein Beispiel für Abhängigkeitsinjektion in Drupal 9. Wir haben bereits zuvor Factory Pattern und Singleton Pattern in PHP besprochen.
Derzeit ist die Abhängigkeitsinjektion in Drupal die bevorzugte Methode für den Zugriff auf und die Nutzung von Diensten und sollte wann immer möglich verwendet werden. Anstatt den globalen Services-Container aufzurufen, werden Services eher als Argumente an einen Konstruktor übergeben oder über Setter-Methoden injiziert. Das explizite Übergeben der Dienste, von denen ein Objekt abhängt, wird als Abhängigkeitsinjektion bezeichnet . In einigen Fällen werden Abhängigkeiten explizit in Klassenkonstruktoren übergeben.
Auf dieser Seite finden Sie alle verfügbaren Dienste im Drupal-Kern. Weitere Informationen zu Diensten finden Sie in der Drupal-Dokumentation.
Betrachten wir den Dienst „entity_type.manager“ als Beispiel, um den Titel für den Knoten mit der ID=1 zu erhalten. Um es in unseren benutzerdefinierten Dienst einzufügen, müssen wir nur den Dienstnamen nehmen und ihn als Argument in der Datei my_module_name.services.yml übergeben , wie unten gezeigt:
my_module_name.services.yml
services: my_module_name.helper: class: Drupal\my_module_name\MyModuleHelper arguments: ['@entity_type.manager']
und dann müssen wir in unserer Dienstklasse nur den Dienst in der Methode _ _construct abrufen und in einer Variablen wie dieser speichern:
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(); } }
und dann könnten wir den Entitätstyp-Manager-Dienst verwenden und den Titel des Knotens mit nid = 1 in der Methode getFirstNodeTitle abrufen.
Ein großes Dankeschön an Ankitha Shetty für ihre Einblicke, die uns geholfen haben, den Artikel zu aktualisieren.