O introducere în modelele de proiectare în PHP (și folosirea acesteia în Drupal)
Publicat: 2022-07-05Cineva inteligent a spus odată - Cod bun de codare, reutilizare excelentă.
Dezvoltatorii se trezesc adesea să rezolve același tip de probleme în mod repetat. Reutilizarea codului este cu adevărat Sfântul Graal al dezvoltării software. De asemenea, cui nu-i place să citească (și să scrie) cod bine structurat? Enter - modele de proiectare în PHP.
Modelele de design PHP s-au dovedit a fi extrem de utile pentru dezvoltatori și sunt o mare soluție de probleme. Urmărirea celor mai bune practici este crucială pentru a scrie cod eficient. PHP Design patterns este un concept de programare orientată pe obiecte (OOP) care este acum folosit și în proiectele Drupal 9. Odată cu adoptarea de către Drupal a conceptelor moderne PHP și OOP începând cu versiunea 8, modelele de design pot fi valorificate pentru o programare mai curată și mai robustă. În acest articol, vom discuta câteva modele de design utilizate în mod obișnuit în PHP și cum să folosim modele precum injecțiile de dependență în Drupal.
Încă pe Drupal 7? Citiți acest articol pentru a găsi o listă de verificare la îndemână care vă va ajuta să vă pregătiți pentru migrarea Drupal 9.
Ce sunt modelele de design în PHP?
În ingineria software, un model de proiectare este o soluție generală repetabilă la problemele frecvente în proiectarea software. Design-urile bune orientate pe obiecte ar trebui să fie reutilizabile, menținute și extensibile , iar modelele de design în PHP ar putea fi foarte utile în acest sens. Nu numai că ajută la rezolvarea problemelor, ci implică o modalitate optimă de abordare a provocărilor comune.
De ce să folosiți PHP Design Patterns
Unele dintre cele mai semnificative beneficii ale implementării modelelor de design în PHP sunt:
- Modelele de design PHP ajută la rezolvarea problemelor repetitive cu care se confruntă în timpul dezvoltării
- Utilizarea modelelor de design în PHP face comunicarea dintre designeri și dezvoltatori mai eficientă
- Puteți fi sigur că alți dezvoltatori vă vor înțelege codul, deoarece urmează modele de design
- Respectarea celor mai bune practici ajută la construirea de aplicații mai robuste
- Ajută la dezvoltarea mai rapidă și mai ușoară
Modele de design utilizate pe scară largă în PHP
Modelele de proiectare pot fi folosite în diferite situații pentru a rezolva probleme similare. Există mai mult de 30 de modele de design care pot fi clasificate în trei tipuri - modele creaționale, structurale și comportamentale.
Modele de creație: modele de proiectare care sunt utilizate în mecanismele de creare a obiectelor, pentru a crea obiecte care pot fi decuplate de sistemul care le-a implementat.
Modele structurale: Acest lucru ușurează proiectarea prin identificarea modalităților simple de a realiza relații între entități
Modele de comportament: sunt folosite pentru a gestiona relațiile, responsabilitățile și algoritmii dintre obiecte
Model de fabrică
Un model din fabrică este folosit pentru a construi un obiect. Așa este - construiți un obiect și nu creați un obiect. Când construim obiectul, mai întâi îl creăm și apoi îl inițializam. De obicei, necesită aplicarea unei anumite logici și efectuarea mai multor pași. Prin urmare, este logic să aveți toate acestea într-un singur loc și să le reutilizați oricând trebuie să construiți un obiect nou în același mod. În principiu, aceasta este utilizarea modelului din fabrică.
Este o idee grozavă să avem o interfață pentru fabrica noastră și ca codul nostru să se bazeze pe ea și nu pe o fabrică de beton.
interface FamilyFactoryInterface { public function create() : Family }
Apoi, implementați interfața din fabrică cu următoarea clasă:
class FamilyFactory implements FamilyFactoryInterface { public function create() : Family { $family = new Family(); // initialize your family return $family; } }
Model adaptor
În Adapter Design Pattern, o clasă transformă interfața unei clase în altă clasă. În acest exemplu avem o clasă TextBook care are metodele getTitle() și getAuthor(). Clientul așteaptă o metodă getTitleAndAuthor(). Pentru a „adapta” SimpleBook pentru demoAdapter avem o clasă de adaptor, BookAdapter , care preia o instanță de TextBook și folosește metodele TextBook getTitle() și getAuthor() în propria sa metodă 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/>"; } ?>
Model PHP Singleton
Pentru a limita instanțiarea unei clase la un singur obiect, se folosește un model singleton în PHP. Acest lucru poate fi util atunci când este necesar un singur obiect în sistem. Este logic să permiteți accesul la o singură instanță a unei anumite clase în timp ce proiectați aplicații web. Pentru a preveni crearea explicită de obiecte din clasa modelului Singleton, este utilizat un constructor privat.
<?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()); ?>
Model de observator în PHP
Modelul PHP Observer este folosit pentru a alerta restul sistemului despre anumite evenimente din anumite locuri.
De exemplu, dacă trebuie să creăm un teatru pentru a prezenta filme criticilor. Definim clasa Teatru cu metoda curenta. Înainte de a prezenta filmul, vrem să trimitem mesaje pe telefoanele mobile ale criticilor. Apoi, la mijlocul filmului vrem să oprim filmul timp de 5 minute pentru a lăsa criticii să aibă un interval. În cele din urmă, după sfârșitul filmului, vrem să cerem criticilor să-și lase răspunsul. Deci, în modelul de observator pentru PHP, obiectul observator este notificat doar atunci când starea este schimbată.
Așa arată codul -
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); } }
Model Decorator pentru PHP
Modelul Decorator este folosit atunci când doriți să modificați caracterul unui obiect în timpul execuției și, cu aceasta, să reduceți moștenirile inutile și numărul de clase. Ei bine, se poate explica prin exemple. Să presupunem că avem clase Sofa and Bed și ambele implementează 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 } }
Atât canapelele, cât și paturile au același comportament la somn. Acum, avem nevoie de alte canapele și paturi cu funcționalități suplimentare care să le spună utilizatorilor urmărirea somnului atunci când dorm pe canapele sau paturi. Prin moștenire putem rezolva această problemă exact așa:
class SmartSofa extends Sofa { public function sleep() : void { parent::sleep(); $this->sleepHours(); } } class SmartBed extends Window { public function sleep() : void { parent::sleep(); $this->sleepHours(); } }
Acum avem 4 clase în total. Cu toate acestea, am putea rezolva această problemă cu 3 clase doar cu modelul Decorator. Iată cum:
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);
Aici, am introdus un nou tip de dormitor care acționează ca un proxy, dar cu o funcționalitate suplimentară.
Utilizarea modelelor de design în Drupal 9
Deși există multe modele de design deja stabilite în Drupal înainte de Drupal 9, acum există multe alte modele care anterior nu erau disponibile. Unele dintre aceste modele noi le înlocuiesc complet pe cele mai vechi, în timp ce altele introduc câteva funcții noi în Drupal 9.
Modelele de design utilizate în Drupal 9 includ:
- Model de programare orientată pe obiecte (OOP)
- Injecții de dependență
- Model de fabrică
- Model Singleton
OOP nu este cu adevărat un singur model, ci o modalitate complet radicală de conceptualizare și structurare a codului, care depășește cu mult modelele de design. Este baza pentru o mulțime de modele de proiectare software populare utilizate astăzi, inclusiv cele utilizate în Drupal 9. A fost introdus în Drupal 7, dar nu a fost utilizat pe scară largă și nu a fost necesar. Situația din Drupal 9 este acum diferită, este utilizat pe scară largă și este necesar.
Injecții de dependență
Injecția de dependență este un model de proiectare a software-ului care vă va permite să eliminați dependențele codificate și, de asemenea, să le faceți posibilă modificarea fie în timpul rulării, fie în timpul compilării. Adăugarea injecției de dependență este ușoară și nu se amestecă în codul dvs. existent. Drupal 8 a introdus conceptul de servicii pentru a decupla funcționalitățile reutilizabile. core.services.yml este un exemplu de injectare a dependenței în Drupal 9. Am discutat deja despre Modelul Factory și Modelul Singleton în PHP.
În prezent, în Drupal, injecția de dependență este metoda preferată pentru accesarea și utilizarea serviciilor și ar trebui folosită ori de câte ori este posibil. În loc să apeleze la containerul de servicii globale, serviciile sunt mai degrabă transmise ca argumente unui constructor sau injectate prin metode de setare. Transmiterea explicită a serviciilor de care depinde un obiect se numește injecție de dependență . În mai multe cazuri, dependențele sunt transmise explicit în constructorii de clasă.
Consultați această pagină pentru a găsi toate serviciile disponibile în core Drupal. Puteți citi mai multe despre servicii în documentația Drupal.
Să luăm în considerare serviciul „entity_type.manager” ca exemplu pentru a obține titlul nodului cu ID=1. Pentru a-l injecta în serviciul nostru personalizat, trebuie doar să luăm numele serviciului și să-l transmitem ca argument în fișierul my_module_name.services.yml , așa cum se arată mai jos:
my_module_name.services.yml
services: my_module_name.helper: class: Drupal\my_module_name\MyModuleHelper arguments: ['@entity_type.manager']
și apoi în clasa noastră de servicii trebuie doar să obținem serviciul în metoda _ _construct și să îl stocăm într-o variabilă ca aceasta:
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(); } }
și apoi am putea folosi serviciul de gestionare a tipului de entitate și să obținem titlul nodului cu nid=1 în metoda getFirstNodeTitle.
Mulțumim lui Ankitha Shetty pentru cunoștințele sale care ne-au ajutat să actualizăm articolul.