Введение в шаблоны проектирования в PHP (и их использование в Drupal)

Опубликовано: 2022-07-05

Кто-то умный однажды сказал: «Хороший код программиста — отличное повторное использование».
Разработчикам часто приходится неоднократно решать одни и те же проблемы. Повторное использование кода — поистине святой Грааль разработки программного обеспечения. Кроме того, кто не любит читать (и писать) хорошо структурированный код? Enter — шаблоны проектирования в PHP.

Шаблоны PHP Design оказались чрезвычайно полезными для разработчиков и позволяют решать огромные проблемы. Следование лучшим практикам имеет решающее значение для написания эффективного кода. Паттерны проектирования PHP — это концепция объектно-ориентированного программирования (ООП), которая теперь также используется в проектах Drupal 9. С внедрением в Drupal современных концепций PHP и ООП, начиная с версии 8, можно использовать шаблоны проектирования для более чистого и надежного программирования. В этой статье мы обсудим несколько часто используемых шаблонов проектирования в PHP и как использовать такие шаблоны, как внедрение зависимостей в Drupal.

Все еще на Друпал 7? Прочтите эту статью, чтобы найти удобный контрольный список, который поможет вам подготовиться к переходу на Drupal 9.

Шаблоны проектирования в PHP

Что такое шаблоны проектирования в PHP?

В программной инженерии шаблон проектирования — это общее повторяемое решение часто возникающей проблемы в разработке программного обеспечения. Хорошие объектно-ориентированные проекты должны быть многоразовыми, поддерживаемыми и расширяемыми , и шаблоны проектирования в PHP могут быть очень полезными в этом. Он не только помогает в решении проблем, но и предполагает оптимальный способ решения общих проблем.

Зачем использовать шаблоны проектирования PHP

Некоторые из наиболее значительных преимуществ реализации шаблонов проектирования в PHP:

  • Шаблоны PHP Design помогают решать повторяющиеся проблемы, возникающие во время разработки.
  • Использование шаблонов проектирования в PHP делает общение между дизайнерами и разработчиками более эффективным.
  • Вы можете быть уверены, что другие разработчики поймут ваш код, поскольку он следует шаблонам проектирования.
  • Следование рекомендациям помогает создавать более надежные приложения
  • Это помогает сделать разработку быстрее и проще

Широко используемые шаблоны проектирования в PHP

Шаблоны проектирования можно использовать в различных ситуациях для решения схожих задач. Существует более 30 паттернов проектирования, которые можно разделить на три типа: творческие, структурные и поведенческие паттерны.

Шаблоны создания: шаблоны проектирования, которые используются в механизмах создания объектов для создания объектов, которые можно отделить от системы, которая их реализовала.

Структурные шаблоны: это упрощает дизайн, определяя простые способы реализации отношений между объектами.

Поведенческие шаблоны: они используются для управления отношениями, обязанностями и алгоритмами между объектами.

Заводской узор

Фабричный шаблон используется для создания объекта. Правильно — строить объект, а не создавать объект. Когда мы создаем объект, мы сначала создаем его, а затем инициализируем. Обычно это требует применения определенной логики и выполнения нескольких шагов. При этом имеет смысл иметь все это в одном месте и повторно использовать его всякий раз, когда вам нужно создать новый объект таким же образом. По сути, это использование фабричного шаблона.
Это отличная идея — иметь интерфейс для нашей фабрики и сделать так, чтобы наш код зависел от него, а не от конкретной фабрики.

 interface FamilyFactoryInterface { public function create() : Family }

Затем реализуйте фабричный интерфейс со следующим классом:

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

Шаблон адаптера

В шаблоне проектирования адаптера класс преобразует интерфейс одного класса в другой класс. В этом примере у нас есть класс TextBook с методами getTitle() и getAuthor(). Клиент ожидает метод getTitleAndAuthor(). Чтобы «адаптировать» SimpleBook для demoAdapter , у нас есть класс адаптера BookAdapter , который принимает экземпляр TextBook и использует методы TextBook getTitle() и getAuthor() в своем собственном методе 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/>"; } ?>

Одноэлементный шаблон PHP

Чтобы ограничить создание экземпляра класса одним объектом, в PHP используется шаблон singleton. Это может быть полезно, когда в системе требуется только один объект. Имеет смысл разрешить доступ только к одному экземпляру определенного класса при разработке веб-приложений. Чтобы предотвратить явное создание объектов из класса шаблонов Singleton, используется закрытый конструктор.

 <?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

Шаблон Decorator используется, когда вы хотите изменить характер объекта во время выполнения и тем самым уменьшить ненужное наследование и количество классов. Ну это можно объяснить на примерах. Допустим, у нас есть классы 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 классами только с помощью паттерна Decorator. Вот как:

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

Здесь мы представили новый тип спящего, который действует как прокси, но с дополнительными функциями.

Использование шаблонов проектирования в Drupal 9

Несмотря на то, что в Drupal уже существовало множество шаблонов проектирования до Drupal 9, сейчас существует гораздо больше шаблонов, которые ранее были недоступны. Некоторые из этих новых шаблонов полностью заменяют старые, в то время как другие вводят в Drupal 9 некоторые новые функции.
Шаблоны проектирования, используемые в Drupal 9, включают:

  • Шаблон объектно-ориентированного программирования (ООП)
  • Внедрение зависимостей
  • Заводской узор
  • Синглтон шаблон

На самом деле ООП — это не единый шаблон, а совершенно радикальный способ концептуализации и структурирования кода, выходящий далеко за рамки шаблонов проектирования. Он является основой для многих популярных шаблонов проектирования программного обеспечения, используемых сегодня, в том числе используемых в Drupal 9. Он был представлен в Drupal 7, но широко не использовался и не требовался. В Drupal 9 сейчас другая ситуация, он широко используется и необходим.

Внедрение зависимостей

Внедрение зависимостей — это шаблон проектирования программного обеспечения, который позволит вам удалить жестко закодированные зависимости, а также позволит изменить их либо во время выполнения, либо во время компиляции. Добавить внедрение зависимостей легко, и оно не мешает вашему существующему коду. В Drupal 8 введена концепция сервисов, чтобы отделить многоразовые функции. core.services.yml — это пример внедрения зависимостей в Drupal 9. Ранее мы уже обсуждали Factory Pattern и Singleton Pattern в PHP.

В настоящее время в Drupal внедрение зависимостей является предпочтительным методом доступа и использования сервисов, и его следует использовать везде, где это возможно. Вместо обращения к контейнеру глобальных служб службы скорее передаются в качестве аргументов конструктору или внедряются через методы установки. Явная передача служб, от которых зависит объект, называется внедрением зависимостей . В некоторых случаях зависимости передаются явно в конструкторах классов.

Посетите эту страницу, чтобы найти все доступные службы в ядре Drupal. Подробнее о сервисах можно прочитать в документации Drupal.

В качестве примера рассмотрим сервис 'entity_type.manager' для получения заголовка узла с ID=1. Чтобы внедрить его в нашу пользовательскую службу, нам просто нужно взять имя службы и передать его в качестве аргумента в файле 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(); } }

а затем мы могли бы использовать службу диспетчера типов объектов и получить заголовок узла с nid=1 в методе getFirstNodeTitle.

Большое спасибо Анките Шетти за ее идеи, которые помогли нам обновить статью.