Что такое структурные паттерны?
В разработке программного обеспечения структурные шаблоны – это шаблоны, которые определяют способы композиции классов или объектов для формирования более крупных структур.
Главная идея структурных шаблонов состоит в том, чтобы обеспечить эффективную организацию классов и объектов в больших программах. Эти шаблоны помогают улучшить модульность, гибкость и расширяемость программного обеспечения, используя некоторые общие концепции и подходы.
Основная идея включает в себя следующее:
- Разделение ответственности: Шаблоны позволяют разделить ответственность между разными частями системы. Это облегчает управление и хранение кода.
- Хранилище знаний: Шаблоны предоставляют стандартизированный способ решения определенных типовых проблем. Это позволяет разработчикам эффективно использовать опыт и готовые решения для реализации собственных проектов.
- Повышение абстракции: Использование шаблонов позволяет разработчикам работать на высшем уровне абстракции, концентрируясь на важных аспектах системы и избегании мелочей.
- Поддержка изменений: Шаблоны позволяют изменять определенные аспекты системы, не изменяя ее общую структуру. Это облегчает внедрение новых функций и адаптацию к изменениям требований.
В целом главная идея структурных шаблонов заключается в создании эффективной и гибкой архитектуры программного обеспечения с помощью стандартизированных подходов и концепций.
Когда использовать?
Структурные шаблоны полезно использовать в следующих случаях:
- Необходимость в разделении ответов: Когда в системе есть несколько компонентов, которые должны работать вместе, но выполняют разные функции, структурные шаблоны могут помочь эффективно разделить ответственность между этими компонентами.
- Потребность в сменности и расширении: Если вы ожидаете, что ваша система будет подвергаться изменениям или расширениям в будущем, использование структурных шаблонов может сделать ваш код более гибким и более легким в поддержке.
- Решение типовых проблем: Если вы сталкиваетесь с типичными проблемами в процессе проектирования и разработки, существует вероятность, что для них уже существуют стандартные решения в виде структурных шаблонов.
- Необходимость в повышении читабельности и повторном использовании кода:Использование структурных шаблонов может облегчить понимание кода и способствовать его повторному использованию, поскольку они предоставляют стандартизированные подходы к решению определенных проблем.
- Избегание дублирования кода: Использование структурных шаблонов может помочь избежать дублирования кода, поскольку они предоставляют общие решения, которые можно использовать в разных частях системы.
На изображении можно увидеть существующие структурные паттерны.
Сегодня поговорим о следующих паттернах:
- Адаптер (Adapter)
- Фасад (Facade)
- Заместитель (Proxy)
Adapter
Этот паттерн позволяет объектам с несовместимыми интерфейсами работать вместе.
Адаптирует интерфейс одного класса в другой, ожидаемый клиентом. Адаптер обеспечивает работу классов с несовместимыми интерфейсами, и часто применяется тогда, когда система поддерживает нужные данные и поведение, но имеет неподходящий интерфейс.
Шаги реализации:
- Убедитесь, что у вас есть два класса с неудобными интерфейсами:
- полезный сервис — служебный класс, который вы не можете изменять (он либо посторонний, либо от него зависит другой код);
- один или несколько клиентов — существующие классы программы, которые не могут использовать сервис из-за несовместимого с ним интерфейса.
- Опишите клиентский интерфейс, через который классы программ могли использовать класс сервиса.
- Создайте класс адаптера, реализовав этот интерфейс.
- Разместите в адаптере поле, содержащее ссылку на объект сервиса. Обычно это поле заполняют объектом, переданным в конструктор адаптера. Но этот объект можно передавать непосредственно к методам адаптера.
- Реализуйте все способы клиентского интерфейса в адаптере. Адаптер обязан делегировать основную работу сервиса.
- Программа должна использовать адаптер только через интерфейс клиента. Это позволит легко изменять и добавлять адаптеры в будущем.
<?php
class Target
{
public function request(): string
{
return "Target: The default target's behavior.";
}
}
class Adaptee
{
public function specificRequest(): string
{
return ".eetpadA eht fo roivaheb laicepS";
}
}
class Adapter extends Target
{
private $adaptee;
public function __construct(Adaptee $adaptee)
{
$this->adaptee = $adaptee;
}
public function request(): string
{
return "Adapter: (TRANSLATED) " . strrev($this->adaptee->specificRequest());
}
}
function clientCode(Target $target)
{
echo $target->request();
}
echo "Client: I can work just fine with the Target objects:\n";
$target = new Target();
clientCode($target);
echo "\n\n";
$adaptee = new Adaptee();
echo "Client: The Adaptee class has a weird interface. See, I don't understand it:\n";
echo "Adaptee: " . $adaptee->specificRequest();
echo "\n\n";
echo "Client: But I can work with it via the Adapter:\n";
$adapter = new Adapter($adaptee);
clientCode($adapter);
Facade
Структурный паттерн проектирования, который предоставляет простой интерфейс к сложной системе классов, библиотеки или фреймворку.
Фасад – это простой интерфейс для работы со сложной подсистемой, содержащей множество классов. Фасад может быть упрощенным отображением системы, не имеющей 100% той функциональности, которой можно было бы добиться, используя сложную подсистему напрямую. Вместе с тем он предоставляет именно тот функционал, который нужен клиенту, и скрывает все остальное.
Шаги реализации:
- Фасад дает быстрый доступ к определенной функциональности подсистемы. Он знает, каким классам нужно переадресовать запрос, и какие данные для этого нужны.
- Дополнительный фасад можно ввести, чтобы не загромождать единый фасад разнородной функциональностью. Он может использоваться как клиентом, так и другими фасадами.
- Сложная подсистема имеет множество разных классов. Для того чтобы заставить всех их что-то делать, нужно знать подробности устройства подсистемы, порядок инициализации объектов и другие детали.
Классы подсистемы не знают о существовании фасада и работают друг с другом напрямую. - Клиент использует фасад вместо непосредственной работы с объектами сложной подсистемы.
<?php
class Facade
{
protected $subsystem1;
protected $subsystem2;
public function __construct(
Subsystem1 $subsystem1 = null,
Subsystem2 $subsystem2 = null
) {
$this->subsystem1 = $subsystem1 ?: new Subsystem1();
$this->subsystem2 = $subsystem2 ?: new Subsystem2();
}
public function operation(): string
{
$result = "Facade initializes subsystems:\n";
$result .= $this->subsystem1->operation1();
$result .= $this->subsystem2->operation1();
$result .= "Facade orders subsystems to perform the action:\n";
$result .= $this->subsystem1->operationN();
$result .= $this->subsystem2->operationZ();
return $result;
}
}
class Subsystem1
{
public function operation1(): string
{
return "Subsystem1: Ready!\n";
}
public function operationN(): string
{
return "Subsystem1: Go!\n";
}
}
class Subsystem2
{
public function operation1(): string
{
return "Subsystem2: Get ready!\n";
}
public function operationZ(): string
{
return "Subsystem2: Fire!\n";
}
}
function clientCode(Facade $facade)
{
echo $facade->operation();
}
$subsystem1 = new Subsystem1();
$subsystem2 = new Subsystem2();
$facade = new Facade($subsystem1, $subsystem2);
clientCode($facade);
Proxy
Паттерн проектирования, позволяющий подставлять вместо реальных объектов специальные объекты-заменители. Эти объекты перехватывают вызовы к оригинальному объекту, позволяя сделать что-либо до или после передачи вызова оригиналу.
Шаги реализации:
- Интерфейс сервиса определяет общий интерфейс сервиса и заместителя. Благодаря этому объект заместителя можно использовать там, где ожидается объект сервиса.
- Сервис содержит полезную бизнес-логику.
- Заместитель хранит ссылку на объект сервиса. После того, как заместитель заканчивает свою работу (например, инициализацию, логирование, защиту или другое), он передает вызовы вложенному сервису. Заместитель может сам отвечать за создание и удаление объекта сервиса.
- Клиент работает с объектами через сервисный интерфейс. Благодаря этому его можно обмануть, подменив объект сервиса объектом заместителя.
<?php
interface Subject
{
public function request(): void;
}
class RealSubject implements Subject
{
public function request(): void
{
echo "RealSubject: Handling request.\n";
}
}
class Proxy implements Subject
{
/**
* @var RealSubject
*/
private $realSubject;
public function __construct(RealSubject $realSubject)
{
$this->realSubject = $realSubject;
}
public function request(): void
{
if ($this->checkAccess()) {
$this->realSubject->request();
$this->logAccess();
}
}
private function checkAccess(): bool
{
// Some real checks should go here.
echo "Proxy: Checking access prior to firing a real request.\n";
return true;
}
private function logAccess(): void
{
echo "Proxy: Logging the time of request.\n";
}
}
function clientCode(Subject $subject)
{
$subject->request();
}
echo "Client: Executing the client code with a real subject:\n";
$realSubject = new RealSubject();
clientCode($realSubject);
echo "\n";
echo "Client: Executing the same client code with a proxy:\n";
$proxy = new Proxy($realSubject);
clientCode($proxy);
Рекомендуем публикацию по теме
В заключении: структурные паттерны очень популярны в программировании. Они используются для решения типовых проблем проектирования и позволяют создавать более структурированные, гибкие и легко изменяемые программные решения. Многие структурные паттерны, такие как Adapter, Composite, Decorator, Facade, и другие, являются стандартными в программировании и используются в разных языках программирования и фреймворках.