Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Шаблони проектування.docx
Скачиваний:
4
Добавлен:
25.11.2019
Размер:
73.72 Кб
Скачать

Пристосуванець

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

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

Ідея шаблону «пристосуванець» полягає в тому, щоб реалізувати цільовий об’єкт як композицію допоміжних підоб’єктів двох різновидів.

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

Цим досягається значна економія пам’яті: замість того, щоб зберігати велику частину об’єктів, кожен з яких зберігав би всі параметри стану, достатньо зберігати кілька підоб’єктів різновиду 1 та багато підоб’єктів різновиду 2, кожен з яких займає в пам’яті мало місця.

Метафору цього шаблона можна розглянути і в протилежному напрямку:

великий за розміром підоб’єкт різновиду 2 час від часу «надягає» на себе різні «маски» — підоб’єкти різновиду 2 — та виглядає з точки зору клієнтського коду як різні об’єкти.

Таким чином, підоб’єкт різновиду 1 можна назвати пристосуванцем, а підоб’єкт різновиду 2 — зовнішнім станом.

Друга важлива ідея шаблону полягає в тому, щоб всі пристосуванці зберігалися в окремому наборі (т.зв. пул пристосуванців).

Коли клієнтському коду потрібен певний пристосуванець, він звертається до менеджера пристосуванців; той намагається відшукати його в пулі, а якщо це не вдається — конструює нового пристосуванця та поміщає його в пул.

Декоратор

Шаблон призначений для під’єднання до об’єкту додаткової поведінки.

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

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

Тоді клієнту замість об’єкту базового класу можна дати об’єкт підкласу: успадкованість від базового класу забезпечує сумісність інтерфейсів, і клієнт, як відомо, «не помічає» підстановки об’єкту підкласу замість об’єкту базового класу.

Однак цей стандартний засіб має недолік: він недостатньо гнучкий.

Конкретний варіант поведінки об’єкта визначається його класом, отже фіксується в момент його створення і за час життя об’єкту змінюватися не може.

Шаблон «декоратор» дозволяє за час життя об’єкту динамічно вносити невеликі зміни в його поведінку, скасовувати такі зміни або комбінувати між собою по кілька таких змін.

Принцип реалізації шаблону

Нехай клас, що реалізує базову функціональність, називається ConcreteComponent (див. рис.). Оголошення його інтерфейсу (тобто оголошення всіх методів) винесемо в повнісню абстрактний базовий клас Component.

Окрім класу ConcreteComponent, інерфейс Component реалізується ще класом Decorator, в якому додається посилання на об’єкт з інтерфейсом Component (будемо називати його цільовим об’єктом; змістовно, це той об’єкт, до функціональності якого об’єкт-декоратор вносить зміну), а всі методи з інтерфейсу мають тривіальні означення — делегуються цільовому об’єкту.

Кожній конкретній малій зміні поведінки відповідає окремий підклас класу Decorator, в якому одна з операцій має своє, відміне від базового, означення, складовою частиною якого є зазвичай і делегування цього методу цільовому об’єкту.

Нехай дано деякий об’єкт a0 класу ConcreteComponent.

Щоб змінити його поведінку, достатньо створити об’єкт a1 деякого з декораторних класів (тобто породжених від класу Decorator), щоб його посилання «цільовий об’єкт» посилалося на об’єкт a0, та використовувати далі a1 замість a0.

Розглянемо поведінку об’єкта a1.

Всі методи (скажімо, f, g) крім деякого методу h просто делегуються об’єкту a0, тому відносно методів f та g поведінка a1 не відрізняється від поведінки a0.

Якщо ж для об’єкту a1 викликати метод h, що має власне означення в декораторному класі, то він відпрацює по-своєму, хоча і звернеться до реалізації даного методу цільовим об’єктом a0.

Висока гнучкість даного підходу забезпечується тим, що в ролі цільового може виступати будь-який об’єкт, що реалізовує інтерфейс Component, в тому числі і об’єкт декораторного класу.

Візьмемо об’єкт a2 такого декораторного класу, який перевизначає метод f, і нехай його цільовим об’єктом є a1. Тоді об’єкт a2 продемонструє модифіковану поведінку відносно методу f (бо він перевизначений в класі самого об’єкта) та відносно методу h (оскільки він перевизначений в об’єкті a1), а поведінка відносно методу g залишиться без змін.

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

Декоратори, «навішені» на оригінальний об’єкт, можна додавати, вилучати та змінювати динамічно, не зачіпаючи при цьому оригінал.

#include <iostream>

#include <memory>

class IComponent {

public:

virtual void operation() = 0;

};

class Component : public IComponent {

public:

virtual void operation() {

std::cout<<"World!"<<std::endl;

}

};

class DecoratorOne : public IComponent {

std::auto_ptr<IComponent> m_component;

public:

DecoratorOne(IComponent * component): m_component(component) {}

virtual void operation() {

std::cout << ", ";

m_component->operation();

}

};

class DecoratorTwo : public IComponent {

std::auto_ptr<IComponent> m_component;

public:

DecoratorTwo(IComponent * component): m_component(component) {}

virtual void operation() {

std::cout << "Hello";

m_component->operation();

}

};

int main() {

DecoratorTwo obj(new DecoratorOne(new Component()));

obj.operation(); // prints "Hello, World!\n"

return 0;