Wprowadzenie do wzorców projektowych w PHP (i wykorzystanie go w Drupalu)

Opublikowany: 2022-07-05

Ktoś mądry kiedyś powiedział: Dobry kod kodera, świetne ponowne wykorzystanie.
Deweloperzy często wielokrotnie rozwiązują problemy tego samego rodzaju. Ponowne wykorzystanie kodu jest naprawdę świętym Graalem tworzenia oprogramowania. A kto nie lubi czytać (i pisać) dobrze ustrukturyzowanego kodu? Enter - wzorce projektowe w PHP.

Wzorce projektowe PHP okazały się niezwykle przydatne dla programistów i świetnie rozwiązują problemy. Przestrzeganie najlepszych praktyk ma kluczowe znaczenie dla pisania wydajnego kodu. Wzorce projektowe PHP to koncepcja programowania zorientowanego obiektowo (OOP), która jest obecnie używana również w projektach Drupal 9. Dzięki przyjęciu przez Drupala nowoczesnych koncepcji PHP i OOP od wersji 8, wzorce projektowe mogą być wykorzystywane do czystszego i bardziej niezawodnego programowania. W tym artykule omówimy kilka powszechnie używanych wzorców projektowych w PHP oraz jak używać wzorców takich jak wstrzykiwanie zależności w Drupalu.

Nadal korzystasz z Drupala 7? Przeczytaj ten artykuł, aby znaleźć przydatną listę kontrolną, która pomoże Ci przygotować się do migracji do Drupala 9.

Wzorce projektowe w PHP

Czym są wzorce projektowe w PHP?

W inżynierii oprogramowania wzorzec projektowy jest ogólnym, powtarzalnym rozwiązaniem powszechnie występującego problemu w projektowaniu oprogramowania. Dobre projekty zorientowane obiektowo powinny być wielokrotnego użytku, konserwowalne i rozszerzalne , a wzorce projektowe w PHP mogą być w tym bardzo pomocne. Nie tylko pomaga w rozwiązywaniu problemów, ale zakłada optymalny sposób radzenia sobie ze wspólnymi wyzwaniami.

Dlaczego warto korzystać z wzorców projektowych PHP

Niektóre z najważniejszych korzyści z implementacji wzorców projektowych w PHP to:

  • Wzorce projektowe PHP pomagają rozwiązywać powtarzające się problemy podczas projektowania
  • Korzystanie z wzorców projektowych w PHP usprawnia komunikację między projektantami a programistami
  • Możesz mieć pewność, że inni programiści zrozumieją Twój kod, ponieważ jest on zgodny z wzorcami projektowymi
  • Postępowanie zgodnie z najlepszymi praktykami pomaga budować bardziej niezawodne aplikacje
  • Pomaga przyspieszyć i ułatwić rozwój

Powszechnie stosowane wzorce projektowe w PHP

Wzorców projektowych można używać w różnych sytuacjach do rozwiązywania podobnych problemów. Istnieje ponad 30 wzorców projektowych, które można ogólnie podzielić na trzy typy - wzorce kreacyjne, strukturalne i behawioralne.

Wzorce tworzenia: Wzorce projektowe używane w mechanizmach tworzenia obiektów w celu tworzenia obiektów, które można oddzielić od systemu, który je zaimplementował.

Wzorce strukturalne: Ułatwia to projektowanie, identyfikując proste sposoby realizacji relacji między podmiotami

Wzorce behawioralne: Służą do zarządzania relacjami, obowiązkami i algorytmami między obiektami

Wzór fabryczny

Do budowy obiektu używany jest wzorzec fabryki. Zgadza się — zbuduj obiekt, a nie twórz obiekt. Kiedy budujemy obiekt, najpierw go tworzymy, a następnie inicjujemy. Zwykle wymaga zastosowania określonej logiki i wykonania wielu kroków. Dzięki temu sensowne jest posiadanie tego wszystkiego w jednym miejscu i ponowne wykorzystanie, gdy trzeba zbudować nowy obiekt w ten sam sposób. Zasadniczo jest to użycie wzorca fabrycznego.
Świetnym pomysłem jest posiadanie interfejsu dla naszej fabryki i uzależnianie naszego kodu od niego, a nie od konkretnej fabryki.

 interface FamilyFactoryInterface { public function create() : Family }

Następnie zaimplementuj interfejs fabryczny z następującą klasą:

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

Wzór adaptera

We wzorcu projektowym adaptera klasa przekształca interfejs jednej klasy w inną klasę. W tym przykładzie mamy klasę TextBook , która ma metody getTitle() i getAuthor(). Klient oczekuje metody getTitleAndAuthor(). Aby „dostosować” SimpleBook do demoAdapter , mamy klasę adaptera BookAdapter , która pobiera instancję TextBook i używa metod TextBook getTitle() i getAuthor() we własnej metodzie 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/>"; } ?>

Wzorzec PHP Singleton

Aby ograniczyć tworzenie instancji klasy do pojedynczego obiektu, używany jest wzorzec singleton w PHP. Może to być przydatne, gdy w systemie potrzebny jest tylko jeden obiekt. Przy projektowaniu aplikacji internetowych sensowne jest umożliwienie dostępu tylko do jednej instancji określonej klasy. Aby zapobiec jawnemu tworzeniu obiektów z klasy wzorca Singleton, wykorzystywany jest konstruktor prywatny.

 <?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()); ?>

Wzór obserwatora w PHP

Wzorzec PHP Observer służy do ostrzegania reszty systemu o określonych zdarzeniach w określonych miejscach.
Na przykład, jeśli musimy stworzyć teatr , aby pokazywać filmy krytykom. Klasę Teatr definiujemy obecną metodą. Przed prezentacją filmu chcemy wysłać wiadomości na telefony komórkowe krytyków. Następnie w połowie filmu chcemy zatrzymać film na 5 minut, aby pozwolić krytykom na przerwę. Na koniec, po zakończeniu filmu, chcemy poprosić krytyków o pozostawienie odpowiedzi. Tak więc we wzorcu obserwatora dla PHP obiekt obserwatora jest powiadamiany tylko wtedy, gdy zmieni się status.

Tak wygląda kod -

 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); } }

Wzór dekoratora dla PHP

Wzorzec Decorator jest używany, gdy chcesz zmienić charakter obiektu w czasie wykonywania, a dzięki temu zmniejszyć niepotrzebne dziedziczenie i liczbę klas. Cóż, można to wyjaśnić na przykładach. Załóżmy, że mamy klasy Sofa i Łóżko, które implementują interfejs 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 } }

Zarówno sofy, jak i łóżka zachowują się w ten sam sposób podczas snu. Teraz potrzebujemy innych sof i łóżek z dodatkową funkcjonalnością, która będzie informować użytkowników o śledzeniu snu, kiedy śpią na sofach lub łóżkach. Dziedziczeniem możemy rozwiązać ten problem tak:

 class SmartSofa extends Sofa { public function sleep() : void { parent::sleep(); $this->sleepHours(); } } class SmartBed extends Window { public function sleep() : void { parent::sleep(); $this->sleepHours(); } }


Teraz mamy w sumie 4 klasy. Jednak moglibyśmy rozwiązać ten problem za pomocą 3 klas tylko za pomocą wzorca Decorator. Oto jak:

 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);

Tutaj wprowadziliśmy nowy rodzaj podkładu, który działa jak proxy, ale z dodatkową funkcjonalnością.

Wykorzystanie wzorców projektowych w Drupalu 9

Chociaż istnieje wiele wzorców projektowych już ustanowionych w Drupalu przed Drupalem 9, teraz istnieje wiele innych wzorców, które były wcześniej niedostępne. Niektóre z tych nowych wzorców całkowicie zastępują starsze, podczas gdy inne wprowadzają nowe funkcje do Drupala 9.
Wzorce projektowe użyte w Drupal 9 obejmują:

  • Wzorzec programowania zorientowanego obiektowo (OOP)
  • Zastrzyki zależności
  • Wzór fabryczny
  • Jednotonowy wzór

OOP nie jest tak naprawdę pojedynczym wzorcem, ale całkowicie radykalnym sposobem konceptualizacji i strukturyzacji kodu, który wykracza daleko poza wzorce projektowe. Jest podstawą wielu popularnych obecnie wzorców projektowania oprogramowania, w tym tych używanych w Drupal 9. Został wprowadzony w Drupal 7, ale nie był szeroko stosowany i nie był wymagany. Sytuacja w Drupalu 9 jest teraz inna, jest on szeroko stosowany i jest wymagany.

Zastrzyki zależności

Wstrzykiwanie zależności to wzorzec projektowania oprogramowania, który umożliwiłby usunięcie zakodowanych na sztywno zależności, a także umożliwiłby ich zmianę w czasie wykonywania lub w czasie kompilacji. Dodanie wstrzykiwania zależności jest łatwe i nie ingeruje w istniejący kod. Drupal 8 wprowadził pojęcie usług w celu oddzielenia funkcjonalności wielokrotnego użytku. core.services.yml to przykład wstrzykiwania zależności w Drupalu 9. Wzorzec Factory i Singleton w PHP omówiliśmy już wcześniej.

Obecnie w Drupalu wstrzykiwanie zależności jest preferowaną metodą uzyskiwania dostępu do usług i korzystania z nich i powinno być używane, gdy tylko jest to możliwe. Zamiast wywoływać globalny kontener usług, usługi są raczej przekazywane jako argumenty do konstruktora lub wstrzykiwane za pomocą metod ustawiających. Jawne przekazywanie usług, od których zależy obiekt, jest nazywane wstrzykiwaniem zależności . W kilku przypadkach zależności są przekazywane jawnie w konstruktorach klas.

Sprawdź tę stronę, aby znaleźć wszystkie dostępne usługi w rdzeniu Drupala. Więcej o usługach przeczytasz w dokumentacji Drupala.

Rozważmy usługę 'entity_type.manager' jako przykład, aby uzyskać tytuł dla węzła o ID=1. Aby wstrzyknąć go do naszej niestandardowej usługi, wystarczy wziąć nazwę usługi i przekazać ją jako argument w pliku my_module_name.services.yml , jak pokazano poniżej:

nazwa_mojego_modułu.usługi.yml

 services: my_module_name.helper: class: Drupal\my_module_name\MyModuleHelper arguments: ['@entity_type.manager']

a następnie w naszej klasie usług wystarczy pobrać usługę w metodzie __construct i zapisać ją w zmiennej takiej jak ta:

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(); } }

a następnie moglibyśmy użyć usługi menedżera typów jednostek i uzyskać tytuł węzła z nid=1 w metodzie getFirstNodeTitle.

Ogromne podziękowania dla Ankithy Shetty za jej spostrzeżenia, które pomogły nam zaktualizować artykuł.