Структурні патерни

Структурні патерни

  • 18 квітня
Денис Розганяєв
Денис Розганяєв Software Engineer у Mobilunity, Викладач Комп'ютерної школи Hillel.

Що таке структурні патерни?

У розробці програмного забезпечення структурні шаблони — це шаблони, які визначають способи композиції класів або об’єктів для формування більших структур. 

Головна ідея структурних шаблонів полягає в тому, щоб забезпечити ефективну організацію класів та об'єктів у великих програмах. Ці шаблони допомагають покращити модульність, гнучкість та розширюваність програмного забезпечення шляхом використання деяких загальних концепцій та підходів.

Основна ідея включає в себе наступне:

  1. Розділення відповідальностей: Шаблони дозволяють розділити відповідальності між різними частинами системи. Це полегшує управління та збереження коду.
  2. Сховище знань: Шаблони надають стандартизований спосіб для вирішення певних типових проблем. Це дозволяє розробникам ефективно використовувати досвід колег та готові рішення для реалізації власних проектів.
  3. Підвищення абстракції: Використання шаблонів дозволяє розробникам працювати на вищому рівні абстракції, концентруючись на важливих аспектах системи та уникненні дрібниць.
  4. Підтримка змін: Шаблони дозволяють змінювати певні аспекти системи, не змінюючи її загальної структури. Це полегшує впровадження нових функцій та адаптацію до змін вимог.

Загалом, головна ідея структурних шаблонів полягає в створенні ефективної та гнучкої архітектури програмного забезпечення за допомогою стандартизованих підходів та концепцій.

Коли використовувати?

Структурні шаблони корисно використовувати в таких випадках:

  1. Необхідність у розділенні відповідальностей: Коли в системі є кілька компонентів, які повинні працювати разом, але виконують різні функції, структурні шаблони можуть допомогти ефективно розділити відповідальності між цими компонентами.
  2. Потреба у змінності та розширенні: Якщо ви очікуєте, що ваша система буде піддаватися змінам або розширенням у майбутньому, використання структурних шаблонів може зробити ваш код більш гнучким та легшим у підтримці.
  3. Вирішення типових проблем: Якщо ви стикаєтесь з типовими проблемами в процесі проектування та розробки, існують ймовірності, що для них вже існують стандартні рішення у вигляді структурних шаблонів.
  4. Необхідність у підвищенні читабельності та повторному використанні коду: Використання структурних шаблонів може полегшити розуміння коду та сприяти його повторному використанню, оскільки вони надають стандартизовані підходи до розв'язання певних проблем.
  5. Уникнення дублювання коду: Використання структурних шаблонів може допомогти уникнути дублювання коду, оскільки вони надають загальні рішення, які можна використовувати у різних частинах системи.

На зображенні можна побачити які є структурні патерни:

Сьогодні поговоримо про наступні патерни:

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

Цей патерн дає змогу об’єктам із несумісними інтерфейсами працювати разом.

Адаптує інтерфейс одного класу в інший, очікуваний клієнтом. Адаптер забезпечує роботу класів з несумісними інтерфейсами, та найчастіше застосовується тоді, коли система підтримує необхідні дані і поведінку, але має невідповідний інтерфейс.

Кроки реалізації:

  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

Cтруктурний патерн проектування, який надає простий інтерфейс до складної системи класів, бібліотеки або фреймворку.

Фасад — це простий інтерфейс для роботи зі складною підсистемою, яка містить безліч класів. Фасад може бути спрощеним відображенням системи, що не має 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, та інші, є стандартними в програмуванні і використовуються в різних мовах програмування та фреймворках.

Рекомендуємо курс по темі