Une introduction aux modèles de conception en PHP (et à leur exploitation dans Drupal)
Publié: 2022-07-05Quelqu'un d'intelligent a dit un jour - Un bon code de codeur, une excellente réutilisation.
Les développeurs se retrouvent souvent à résoudre le même type de problèmes à plusieurs reprises. La réutilisation du code est véritablement le Saint Graal du développement logiciel. Aussi, qui n'aime pas lire (et écrire) du code bien structuré ? Entrez - modèles de conception en PHP.
Les modèles de conception PHP se sont avérés extrêmement utiles pour les développeurs et sont un énorme résolveur de problèmes. Suivre les meilleures pratiques est crucial pour écrire un code efficace. Les modèles de conception PHP sont un concept de programmation orientée objet (POO) qui est désormais également utilisé dans les projets Drupal 9. Avec l'adoption par Drupal des concepts PHP et OOP modernes depuis la version 8, les modèles de conception peuvent être exploités pour une programmation plus propre et plus robuste. Dans cet article, nous aborderons quelques modèles de conception couramment utilisés en PHP et comment utiliser des modèles tels que les injections de dépendances dans Drupal.
Toujours sur Drupal 7 ? Lisez cet article pour trouver une liste de contrôle pratique qui vous aidera à préparer une migration Drupal 9.
Que sont les Design Patterns en PHP ?
En génie logiciel, un modèle de conception est une solution générale reproductible à un problème courant dans la conception de logiciels. De bonnes conceptions orientées objet doivent être réutilisables, maintenables et extensibles et les modèles de conception en PHP pourraient être très utiles pour ce faire. Cela n'aide pas seulement à résoudre les problèmes, cela implique une manière optimale de relever les défis communs.
Pourquoi utiliser les modèles de conception PHP
Certains des avantages les plus importants de l'implémentation de modèles de conception en PHP sont :
- Les modèles de conception PHP aident à résoudre les problèmes répétitifs rencontrés lors du développement
- L'utilisation de modèles de conception en PHP rend la communication entre les concepteurs et les développeurs plus efficace
- Vous pouvez être sûr que les autres développeurs comprendront votre code car il suit les modèles de conception
- Suivre les meilleures pratiques permet de créer des applications plus robustes
- Cela aide à rendre le développement plus rapide et plus facile
Modèles de conception largement utilisés en PHP
Les modèles de conception peuvent être utilisés dans diverses situations pour résoudre des problèmes similaires. Il existe plus de 30 modèles de conception qui peuvent être classés en trois types - modèles de création, structurels et comportementaux.
Modèles de création : modèles de conception utilisés dans les mécanismes de création d'objets, pour créer des objets pouvant être découplés du système qui les a implémentés.
Modèles structurels : cela facilite la conception en identifiant des moyens simples de réaliser des relations entre les entités
Modèles comportementaux : ils sont utilisés pour gérer les relations, les responsabilités et les algorithmes entre les objets
Modèle d'usine
Un modèle d'usine est utilisé pour construire un objet. C'est vrai - construisez un objet et ne créez pas d'objet. Lorsque nous construisons l'objet, nous le créons d'abord, puis nous l'initialisons. Habituellement, cela nécessite d'appliquer une certaine logique et d'effectuer plusieurs étapes. Avec cela, il est logique d'avoir tout cela au même endroit et de le réutiliser chaque fois que vous avez besoin de faire construire un nouvel objet de la même manière. Fondamentalement, c'est l'utilisation du modèle d'usine.
C'est une excellente idée d'avoir une interface pour notre usine et que notre code dépende d'elle et non d'une usine en béton.
interface FamilyFactoryInterface { public function create() : Family }
Ensuite, implémentez l'interface d'usine avec la classe suivante :
class FamilyFactory implements FamilyFactoryInterface { public function create() : Family { $family = new Family(); // initialize your family return $family; } }
Modèle d'adaptateur
Dans Adapter Design Pattern, une classe transforme l'interface d'une classe en une autre classe. Dans cet exemple, nous avons une classe TextBook qui a les méthodes getTitle() et getAuthor(). Le client attend une méthode getTitleAndAuthor(). Pour "adapter" SimpleBook pour demoAdapter, nous avons une classe d'adaptateur, BookAdapter , qui prend une instance de TextBook et utilise les méthodes TextBook getTitle() et getAuthor() dans sa propre méthode getTitleAndAuthor.
<?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/>"; } ?>
Modèle de singleton PHP
Afin de limiter l'instanciation d'une classe à un seul objet, un modèle singleton en PHP est utilisé. Cela peut être utile lorsqu'un seul objet est nécessaire dans le système. Il est logique de n'autoriser l'accès qu'à une seule instance d'une certaine classe lors de la conception d'applications Web. Afin d'empêcher la création explicite d'objets à partir de la classe de modèle Singleton, un constructeur privé est utilisé.
<?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()); ?>
Modèle d'observateur en PHP
Le modèle PHP Observer est utilisé pour alerter le reste du système sur des événements particuliers à certains endroits.
Par exemple, si nous devons créer un théâtre pour montrer des films aux critiques. Nous définissons la classe Théâtre avec la méthode courante. Avant de présenter le film, nous voulons envoyer des messages aux téléphones portables des critiques. Ensuite, au milieu du film, nous voulons arrêter le film pendant 5 minutes pour laisser un temps d'arrêt aux critiques. Enfin, après la fin du film, nous voulons demander aux critiques de laisser leur réponse. Ainsi, dans le modèle d'observateur pour PHP, l'objet observateur n'est averti que lorsque le statut est modifié.
Voici à quoi ressemble le code -
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); } }
Modèle de décorateur pour PHP
Le modèle Decorator est utilisé lorsque vous souhaitez modifier le caractère d'un objet au moment de l'exécution, et avec cela, réduire les héritages inutiles et le nombre de classes. Eh bien, cela peut être expliqué avec des exemples. Disons que nous avons les classes Sofa et Bed, et qu'elles implémentent toutes deux 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 } }
Les canapés et les lits ont le même comportement pour dormir. Maintenant, nous avons besoin d'autres canapés et lits avec des fonctionnalités supplémentaires qui indiqueront aux utilisateurs le suivi du sommeil lorsqu'ils dorment sur les canapés ou les lits. Avec l'héritage, nous pouvons résoudre ce problème comme ceci :
class SmartSofa extends Sofa { public function sleep() : void { parent::sleep(); $this->sleepHours(); } } class SmartBed extends Window { public function sleep() : void { parent::sleep(); $this->sleepHours(); } }
Nous avons maintenant 4 classes au total. Cependant, nous pourrions résoudre ce problème avec 3 classes uniquement avec le pattern Decorator. Voici comment:
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);
Ici, nous avons introduit un nouveau type de dormeur qui agit comme un proxy mais avec une fonctionnalité supplémentaire en plus.
Tirer parti des modèles de conception dans Drupal 9
Bien qu'il existe de nombreux modèles de conception déjà établis dans Drupal avant Drupal 9, il existe maintenant de nombreux autres modèles qui n'étaient pas disponibles auparavant. Certains de ces nouveaux modèles remplacent complètement les anciens, tandis que d'autres introduisent de nouvelles fonctionnalités dans Drupal 9.
Les modèles de conception utilisés dans Drupal 9 incluent :
- Modèle de programmation orienté objet (POO)
- Injections de dépendance
- Modèle d'usine
- Modèle de singleton
La POO n'est pas vraiment un modèle unique, mais une manière complètement radicale de conceptualiser et de structurer du code qui va bien au-delà des modèles de conception. C'est la base de nombreux modèles de conception de logiciels populaires utilisés aujourd'hui, y compris ceux utilisés dans Drupal 9. Il a été introduit dans Drupal 7, mais il n'a pas été largement utilisé et n'était pas nécessaire. La situation dans Drupal 9 est maintenant différente, il est largement utilisé et requis.
Injections de dépendance
L'injection de dépendances est un modèle de conception logicielle qui vous permettrait de supprimer les dépendances codées en dur et de les modifier soit au moment de l'exécution, soit au moment de la compilation. L'ajout d'une injection de dépendance est facile et ne se mêle pas de votre code existant. Drupal 8 a introduit la notion de services afin de découpler les fonctionnalités réutilisables. core.services.yml est un exemple d'injection de dépendances dans Drupal 9. Nous avons déjà discuté de Factory Pattern et Singleton Pattern en PHP précédemment.
Actuellement, dans Drupal, l'injection de dépendances est la méthode préférée pour accéder et utiliser les services et doit être utilisée chaque fois que possible. Au lieu d'appeler le conteneur de services global, les services sont plutôt transmis en tant qu'arguments à un constructeur ou injectés via des méthodes de définition. La transmission explicite des services dont dépend un objet est appelée injection de dépendance . Dans plusieurs cas, les dépendances sont passées explicitement dans les constructeurs de classe.
Consultez cette page pour trouver tous les services disponibles dans le noyau Drupal. Vous pouvez en savoir plus sur les services dans la documentation Drupal.
Considérons le service 'entity_type.manager' comme exemple pour obtenir le titre du nœud avec ID=1. Afin de l'injecter dans notre service personnalisé, il suffit de prendre le nom du service et de le passer en argument dans le fichier my_module_name.services.yml comme indiqué ci-dessous :
nom_mon_module.services.yml
services: my_module_name.helper: class: Drupal\my_module_name\MyModuleHelper arguments: ['@entity_type.manager']
puis dans notre classe de service, il nous suffit d'obtenir le service dans la méthode _ _construct et de le stocker dans une variable comme celle-ci :
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(); } }
puis nous pourrions utiliser le service de gestionnaire de type d'entité et obtenir le titre du nœud avec nid = 1 dans la méthode getFirstNodeTitle.
Un grand merci à Ankitha Shetty pour ses idées qui nous ont aidés à mettre à jour l'article.