Структурные паттерны

Структурные паттерны

  • 18 апреля
Денис Розганяев
Денис Розганяев Software Engineer в Mobilunity, Преподаватель Компьютерной школы Hillel.

Что такое структурные паттерны?

В разработке программного обеспечения структурные шаблоны – это шаблоны, которые определяют способы композиции классов или объектов для формирования более крупных структур.

Главная идея структурных шаблонов состоит в том, чтобы обеспечить эффективную организацию классов и объектов в больших программах. Эти шаблоны помогают улучшить модульность, гибкость и расширяемость программного обеспечения, используя некоторые общие концепции и подходы.

Основная идея включает в себя следующее:

  1. Разделение ответственности: Шаблоны позволяют разделить ответственность между разными частями системы. Это облегчает управление и хранение кода.
  2. Хранилище знаний: Шаблоны предоставляют стандартизированный способ решения определенных типовых проблем. Это позволяет разработчикам эффективно использовать опыт и готовые решения для реализации собственных проектов.
  3. Повышение абстракции: Использование шаблонов позволяет разработчикам работать на высшем уровне абстракции, концентрируясь на важных аспектах системы и избегании мелочей.
  4. Поддержка изменений: Шаблоны позволяют изменять определенные аспекты системы, не изменяя ее общую структуру. Это облегчает внедрение новых функций и адаптацию к изменениям требований.

В целом главная идея структурных шаблонов заключается в создании эффективной и гибкой архитектуры программного обеспечения с помощью стандартизированных подходов и концепций.


Когда использовать?

Структурные шаблоны полезно использовать в следующих случаях:

  1. Необходимость в разделении ответов: Когда в системе есть несколько компонентов, которые должны работать вместе, но выполняют разные функции, структурные шаблоны могут помочь эффективно разделить ответственность между этими компонентами.
  2. Потребность в сменности и расширении: Если вы ожидаете, что ваша система будет подвергаться изменениям или расширениям в будущем, использование структурных шаблонов может сделать ваш код более гибким и более легким в поддержке.
  3. Решение типовых проблем: Если вы сталкиваетесь с типичными проблемами в процессе проектирования и разработки, существует вероятность, что для них уже существуют стандартные решения в виде структурных шаблонов.
  4. Необходимость в повышении читабельности и повторном использовании кода:Использование структурных шаблонов может облегчить понимание кода и способствовать его повторному использованию, поскольку они предоставляют стандартизированные подходы к решению определенных проблем.
  5. Избегание дублирования кода: Использование структурных шаблонов может помочь избежать дублирования кода, поскольку они предоставляют общие решения, которые можно использовать в разных частях системы.

На изображении можно увидеть существующие структурные паттерны.

Сегодня поговорим о следующих паттернах:

  1. Адаптер (Adapter)
  2. Фасад (Facade)
  3. Заместитель (Proxy)

Adapter

Этот паттерн позволяет объектам с несовместимыми интерфейсами работать вместе.

Адаптирует интерфейс одного класса в другой, ожидаемый клиентом. Адаптер обеспечивает работу классов с несовместимыми интерфейсами, и часто применяется тогда, когда система поддерживает нужные данные и поведение, но имеет неподходящий интерфейс.

Шаги реализации:

  1. Убедитесь, что у вас есть два класса с неудобными интерфейсами:
    1. полезный сервис — служебный класс, который вы не можете изменять (он либо посторонний, либо от него зависит другой код);
    2. один или несколько клиентов — существующие классы программы, которые не могут использовать сервис из-за несовместимого с ним интерфейса.
  2. Опишите клиентский интерфейс, через который классы программ могли использовать класс сервиса.
  3. Создайте класс адаптера, реализовав этот интерфейс.
  4. Разместите в адаптере поле, содержащее ссылку на объект сервиса. Обычно это поле заполняют объектом, переданным в конструктор адаптера. Но этот объект можно передавать непосредственно к методам адаптера.
  5. Реализуйте все способы клиентского интерфейса в адаптере. Адаптер обязан делегировать основную работу сервиса.
  6. Программа должна использовать адаптер только через интерфейс клиента. Это позволит легко изменять и добавлять адаптеры в будущем.
<?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% той функциональности, которой можно было бы добиться, используя сложную подсистему напрямую. Вместе с тем он предоставляет именно тот функционал, который нужен клиенту, и скрывает все остальное.

Шаги реализации:

  1. Фасад дает быстрый доступ к определенной функциональности подсистемы. Он знает, каким классам нужно переадресовать запрос, и какие данные для этого нужны.
  2. Дополнительный фасад можно ввести, чтобы не загромождать единый фасад разнородной функциональностью. Он может использоваться как клиентом, так и другими фасадами.
  3. Сложная подсистема имеет множество разных классов. Для того чтобы заставить всех их что-то делать, нужно знать подробности устройства подсистемы, порядок инициализации объектов и другие детали.
    Классы подсистемы не знают о существовании фасада и работают друг с другом напрямую.
  4. Клиент использует фасад вместо непосредственной работы с объектами сложной подсистемы.
<?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

Паттерн проектирования, позволяющий подставлять вместо реальных объектов специальные объекты-заменители. Эти объекты перехватывают вызовы к оригинальному объекту, позволяя сделать что-либо до или после передачи вызова оригиналу.

Шаги реализации:

  1. Интерфейс сервиса определяет общий интерфейс сервиса и заместителя. Благодаря этому объект заместителя можно использовать там, где ожидается объект сервиса.
  2. Сервис содержит полезную бизнес-логику.
  3. Заместитель хранит ссылку на объект сервиса. После того, как заместитель заканчивает свою работу (например, инициализацию, логирование, защиту или другое), он передает вызовы вложенному сервису. Заместитель может сам отвечать за создание и удаление объекта сервиса.
  4. Клиент работает с объектами через сервисный интерфейс. Благодаря этому его можно обмануть, подменив объект сервиса объектом заместителя.
<?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, и другие, являются стандартными в программировании и используются в разных языках программирования и фреймворках.