Uma introdução aos padrões de design em PHP (e aproveitando-o no Drupal)

Publicados: 2022-07-05

Alguém inteligente disse uma vez - Bom código de codificadores, ótima reutilização.
Os desenvolvedores geralmente resolvem o mesmo tipo de problema repetidamente. Reutilizar código é realmente o santo graal do desenvolvimento de software. Além disso, quem não gosta de ler (e escrever) código bem estruturado? Enter - padrões de design em PHP.

Os padrões de design PHP provaram ser extremamente úteis para os desenvolvedores e são um grande solucionador de problemas. Seguir as melhores práticas é crucial para escrever código eficiente. Os padrões de design PHP são um conceito de programação orientada a objetos (OOP) que agora também é usado em projetos Drupal 9. Com a adoção de conceitos modernos de PHP e OOP pelo Drupal desde a versão 8, os padrões de design podem ser aproveitados para uma programação mais limpa e robusta. Neste artigo, discutiremos alguns padrões de design comumente usados ​​em PHP e como usar padrões como injeções de dependência no Drupal.

Ainda no Drupal 7? Leia este artigo para encontrar uma lista de verificação útil que o ajudará a se preparar para uma migração do Drupal 9.

Padrões de projeto em PHP

O que são Padrões de Projeto em PHP?

Na engenharia de software, um padrão de projeto é uma solução geral repetível para problemas comuns no projeto de software. Bons designs orientados a objetos devem ser reutilizáveis, de fácil manutenção e extensíveis e os Design Patterns em PHP podem ser muito úteis para isso. Não só ajuda na resolução de problemas, mas implica uma forma óptima de abordar os desafios comuns.

Por que usar padrões de design PHP

Alguns dos benefícios mais significativos da implementação de padrões de design em PHP são:

  • Os padrões de design PHP ajudam a resolver problemas repetitivos enfrentados durante o desenvolvimento
  • O uso de padrões de design em PHP torna a comunicação entre designers e desenvolvedores mais eficiente
  • Você pode ter certeza de que outros desenvolvedores entenderão seu código, pois ele segue padrões de design
  • Seguir as melhores práticas ajuda a criar aplicativos mais robustos
  • Ajuda a tornar o desenvolvimento mais rápido e fácil

Padrões de design amplamente usados ​​em PHP

Padrões de projeto podem ser usados ​​em várias situações para resolver problemas semelhantes. Existem mais de 30 padrões de design que podem ser amplamente categorizados em três tipos - padrões criacionais, estruturais e comportamentais.

Padrões de Criação: Padrões de design que são usados ​​em mecanismos de criação de objetos, para criar objetos que podem ser desacoplados do sistema que os implementou.

Padrões Estruturais: Isso facilita o projeto, identificando maneiras simples de realizar relacionamentos entre entidades

Padrões Comportamentais: Eles são usados ​​para gerenciar relacionamentos, responsabilidades e algoritmos entre objetos

Padrão de fábrica

Um padrão de fábrica é usado para construir um objeto. Isso mesmo — construa um objeto e não crie um objeto. Quando construímos o objeto, primeiro o criamos e depois o inicializamos. Normalmente, é necessário aplicar certa lógica e executar várias etapas. Com isso, faz sentido ter tudo isso em um só lugar e reutilizá-lo sempre que precisar ter um novo objeto construído da mesma forma. Fundamentalmente, esse é o uso do padrão de fábrica.
É uma ótima idéia ter uma interface para nossa fábrica e ter nosso código dependente dela e não de uma fábrica concreta.

 interface FamilyFactoryInterface { public function create() : Family }

Em seguida, implemente a interface de fábrica com a seguinte classe:

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

Padrão Adaptador

No Adapter Design Pattern, uma classe transforma a interface de uma classe em outra classe. Neste exemplo temos uma classe TextBook que possui os métodos getTitle() e getAuthor(). O cliente espera um método getTitleAndAuthor(). Para "adaptar" SimpleBook para demoAdapter , temos uma classe adaptadora, BookAdapter , que recebe uma instância de TextBook e usa os métodos getTitle() e getAuthor() de TextBook em seu próprio método 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/>"; } ?>

Padrão PHP Singleton

Para limitar a instanciação de uma classe a um único objeto, um padrão singleton em PHP é usado. Isso pode ser útil quando apenas um objeto é necessário no sistema. Faz sentido permitir o acesso a apenas uma instância de uma determinada classe ao projetar aplicativos da Web. Para evitar a criação explícita de objetos da classe padrão Singleton, um construtor privado é utilizado.

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

Padrão de observador em PHP

O padrão PHP Observer é usado para alertar o restante do sistema sobre eventos específicos em determinados locais.
Por exemplo, se precisarmos criar um Teatro para exibir filmes para a crítica. Definimos a classe Teatro com o método atual. Antes de apresentar o filme, queremos enviar mensagens para os celulares dos críticos. Então, no meio do filme, queremos parar o filme por 5 minutos para que os críticos tenham um intervalo. Finalmente, após o final do filme, queremos pedir aos críticos que deixem sua resposta. Assim, no padrão de observador para PHP, o objeto observador só é notificado quando o status é alterado.

É assim que o código se parece -

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

Padrão Decorador para PHP

O padrão Decorator é usado quando você deseja alterar o caráter de um objeto em tempo de execução e, com isso, reduzir heranças desnecessárias e o número de classes. Bem, isso pode ser explicado com exemplos. Digamos que temos as classes Sofa e Bed, e ambas implementam 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 } }

Tanto os sofás como as camas têm o mesmo comportamento para dormir. Agora, precisamos de outros sofás e camas com funcionalidade adicional que informe aos usuários o rastreamento do sono quando eles dormem nos sofás ou camas. Com herança podemos resolver este problema assim:

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


Agora temos 4 aulas no total. No entanto, poderíamos resolver esse problema com 3 classes apenas com o padrão Decorator. Veja como:

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

Aqui, introduzimos um novo tipo de dorminhoco que funciona como um proxy, mas com uma funcionalidade extra.

Aproveitando os padrões de design no Drupal 9

Embora existam muitos padrões de design já estabelecidos no Drupal antes do Drupal 9, agora existem muitos outros padrões que não estavam disponíveis anteriormente. Alguns desses novos padrões substituem completamente os mais antigos, enquanto outros introduzem alguns novos recursos no Drupal 9.
Os padrões de design usados ​​no Drupal 9 incluem:

  • Padrão de Programação Orientada a Objetos (OOP)
  • Injeções de dependência
  • Padrão de fábrica
  • Padrão Singleton

OOP não é realmente um padrão único, mas uma maneira completamente radical de conceituar e estruturar código que vai muito além de apenas padrões de design. Ele é a base para muitos padrões de projeto de software populares em uso hoje, incluindo aqueles usados ​​no Drupal 9. Ele foi introduzido no Drupal 7, mas não foi usado extensivamente e não foi necessário. A situação no Drupal 9 agora é diferente, é amplamente utilizada e necessária.

Injeções de dependência

A injeção de dependência é um padrão de design de software que permite remover dependências codificadas e também possibilita alterá-las em tempo de execução ou em tempo de compilação. Adicionar injeção de dependência é fácil e não interfere no seu código existente. O Drupal 8 introduziu o conceito de serviços para desacoplar as funcionalidades reutilizáveis. core.services.yml é um exemplo de injeção de dependência no Drupal 9. Já discutimos Factory Pattern e Singleton Pattern em PHP anteriormente.

Atualmente, no Drupal, a injeção de dependência é o método preferencial para acessar e usar serviços e deve ser usado sempre que possível. Em vez de chamar o contêiner de serviços globais, os serviços são passados ​​como argumentos para um construtor ou injetados por meio de métodos setter. A passagem explícita dos serviços dos quais um objeto depende é chamada de injeção de dependência . Em vários casos, as dependências são passadas explicitamente em construtores de classe.

Confira esta página para encontrar todos os serviços disponíveis no núcleo Drupal. Você pode ler mais sobre serviços na documentação do Drupal.

Vamos considerar o serviço 'entity_type.manager' como um exemplo para obter o título do nó com ID=1. Para injetá-lo em nosso serviço personalizado, basta pegar o nome do serviço e passá-lo como argumento no arquivo my_module_name.services.yml conforme mostrado abaixo:

my_module_name.services.yml

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

e então em nossa classe de serviço só temos que pegar o serviço no método _ _construct e armazená-lo em uma variável assim:

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

e então poderíamos usar o serviço de gerenciador de tipo de entidade e obter o título do nó com nid=1 no método getFirstNodeTitle.

Muito obrigado a Ankitha Shetty por seus insights que nos ajudaram a atualizar o artigo.