Декоратор (шаблон проєктування)

Декоратор (фр. décorateur) — структурний шаблон проєктування, призначений для динамічного підключення додаткових можливостей до об'єкта. Шаблон Decorator надає гнучку альтернативу методу визначення підкласів з метою розширення функціональності.

Основні характеристики

Завдання

Об'єкт, який передбачається використовувати, виконує основні функції. Проте може виникнути потреба додати до нього деяку додаткову функціональність, яка виконуватиметься до і/або після основної функціональності об'єкта.

Спосіб вирішення

Декоратор передбачає розширення функціональності об'єкта без визначення підкласів.

Учасники

Клас ConcreteComponent — клас, в який за допомогою шаблону Декоратор додається нова функціональність. В деяких випадках базова функціональність надається класами, похідними від класу ConcreteComponent. У подібних випадках клас ConcreteComponent є вже не абстрактним, а конкретним. Абстрактний клас Component визначає інтерфейс для використання всіх цих класів.

Переваги

  • Декоратори забезпечують гнучку альтернативу підкласу для розширення функціональності
  • Декоратори дозволяють модифікувати поведінку під час виконання, а не повертатися до існуючого коду та вносити зміни
  • Декоратори - це хороше рішення для перестановки завдань, тому що ви можете загорнути компонент з будь-якою кількістю декораторів
  • Шаблон декоратора підтримує принцип, що класи повинні бути відкриті для розширення, але закриті для модифікації

Недоліки

  • Декоратори можуть призвести до багатьох невеликих об'єктів у нашому дизайні, і надмірне використання може бути складним
  • Декоратори можуть викликати проблеми, якщо клієнт сильно залежить від компонентів конкретного типу
  • Декоратори можуть ускладнити процес аналізу компонента, оскільки вам потрібно не лише інвентувати компонент, але і обернути його кількома декораторами
  • Може бути складно, щоб декоратори відслідковували інших декораторів, тому що повертатися назад до декількох шарів ланцюга декораторів починає натискати шаблон декоратора поза його справжнім наміром

Наслідки

Функціональність, що додається, реалізується в невеликих об'єктах. Перевага полягає в можливості динамічно додавати цю функціональність до або після основної функціональності об'єкта ConcreteComponent.

Зв'язок з іншими патернами

  • Стратегія змінює реалізацію, декоратор — доповнює
  • Ланцюжок обов’язків та Декоратор виконують операції через серію пов’язаних об’єктів. Але Ланцюжок обов’язків може виконувати довільні дії, незалежні одна від одної, а також у будь-який момент переривати виконання, а декоратори розширюють певну дію, не ламаючи інтерфейс базової операції і не перериваючи виконання інших декораторів.

Реалізація

Створюється абстрактний клас, що представляє як початковий клас, так і нові функції, що додаються в клас. У класах-декораторах нові функції викликаються в необхідній послідовності — до або після виклику подальшого об'єкта.

C++

Приклад реалізації мовою С++
#include <iostream>

using namespace std;

//абстрактний клас - основа патерну
//декорації підлягає метод do_it()
struct I
{
	virtual ~I() {}
	virtual void do_it() = 0;
};
//"справжній" клас - його метод do_it() мав працювати безумовно
struct A : public I
{
	~A()
	{
		cout << "A dtor" << '\n';
	}
	virtual void do_it()
	{
		cout << 'A';
	}
};
//ще один абстрактний клас - основа майбутніх декорацій-обгорток
class D : public I
{
private:
	I* m_wrappee;
public:
	//декоратор приховує всередині обгорнутий "справжній" об'єкт
	//і перенаправляє йому запити щось зробити
	D(I* inner) : m_wrappee(inner) {}
	~D()
	{
		delete m_wrappee;
	}
	virtual void do_it()
	{
		m_wrappee->do_it();
	}
};
// конкретні реалізації обгорток: спочатку працює вкладений об'єкт, потім - обгортка
struct X : public D
{
	X(I* core) : D(core) {}
	~X()
	{
		cout << "X dtor" << " ";
	}
	virtual void do_it()
	{
		D::do_it();
		cout << 'X';
	}
};
struct Y : public D 
{  
	Y(I* core) : D(core) {}
	~Y()
	{
		cout << "Y dtor" << " ";
	}
	virtual void do_it()
	{
		D::do_it();
		cout << 'Y';
	}
};
struct Z : public D 
{ 
	Z(I* core) : D(core) {}
	~Z()
	{
		cout << "Z dtor" << " ";
	}
	virtual void do_it()
	{
		D::do_it();
		cout << 'Z';
	}
};
void main()
{
	I* anXYZ = new Z(new Y(new X(new A)));
	anXYZ->do_it(); cout << '\n'; // A X Y Z
	delete anXYZ;
	A a;
	I* anX = new X(&a);
	anX->do_it(); cout << '\n'; // A X
}

C#

Приклад реалізації мовою С#
using System;

namespace Decorator
{
    // основа патерну
    interface INotifier
    {
        void Send();
    }
    // "справжній" клас
    // надсилає сповіщення користувачу
    class UserNotifier : INotifier
    {
        public void Send()
        {
            Console.WriteLine("Notify user regularly");
        }
    }

    // абстрактний клас декорацій
    abstract class NotifierDecoratorBase : INotifier
    {
        // декоратор приховує всередині обгорнутий "справжній" об'єкт
        protected INotifier notifier;

        public NotifierDecoratorBase(INotifier notifier)
        {
            this.notifier = notifier;
        }

        // перенаправляє "справжньому" об'єкту запити щось зробити
        public virtual void Send()
        {
            notifier.Send();
        }
    }
    // різноманітні декорації
    // додаткові алгоритми надсилання сповіщень
    class SmsNotifier : NotifierDecoratorBase
    {
        public SmsNotifier(INotifier notifier)
            : base(notifier) { }

        public override void Send()
        {
            // спочатку працює вкладений об'єкт, потім - обгортка
            base.Send();
            Console.WriteLine("Notify user with sms");
        }
    }
    class EmailNotifier : NotifierDecoratorBase
    {
        public EmailNotifier(INotifier notifier)
            : base(notifier) { }

        public override void Send()
        {
            base.Send();
            Console.WriteLine("Notify user with email");
        }
    }

    class Program
    {
        // конфігурації системи
        static bool isSmsNotificationEnabled    = true;
        static bool isEmailNotificationEnabled  = false;

        static void Main(string[] args)
        {
            // створюємо спосіб оповіщення
            INotifier notifier = new UserNotifier();

            // обгортаємо його залежно від налаштувань систему
            if (isSmsNotificationEnabled)   notifier = new SmsNotifier(notifier);
            if (isEmailNotificationEnabled) notifier = new EmailNotifier(notifier);

            // виконуємо дію
            notifier.Send();

            Console.ReadLine();
        }
    }
}

Java

Приклад реалізації мовою Java
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;

abstract class WordSplitter {
    abstract public List<String> getWords(String wordString);
}

/**
Просто перетворює рядок слів, написаних через пробіл, на список слів
*/
class PureWordSplitter extends WordSplitter {

    public PureWordSplitter() {}

    public List<String> getWords(String wordString) {
        String[] wordArray = wordString.split(" ");
        List<String> wordList = new ArrayList<String>(wordArray.length);
	Collections.addAll(wordList, wordArray);
        return wordList;
    }
}

/**
Абстрактний клас для декораторів
*/
abstract class FilteringSplitter extends WordSplitter {
    protected WordSplitter decoratedWordSplitter;

    abstract public List<String> getWords(String wordString);
}

/**
Декоратор, який видаляє слова з не більше ніж двома буквами
*/
class ShortWordsFilteringSplitter extends FilteringSplitter {

    public ShortWordsFilteringSplitter(WordSplitter wordSplitter) {
        decoratedWordSplitter = wordSplitter;
    }

    public List<String> getWords(String wordString) {
        List<String> wordList = decoratedWordSplitter.getWords(wordString);
        for (int i = 0; i < wordList.size();) {
            if (wordList.get(i).length() <= 2) {
                wordList.remove(i);
            } else {
	    	++i;
	    }
        }
        return wordList;
    }
}

/**
Декоратор, який видаляє зі списку слова, що починаються з великої букви
*/
class PropperNamesFilteringSplitter extends FilteringSplitter {

    public PropperNamesFilteringSplitter(WordSplitter wordSplitter) {
        decoratedWordSplitter = wordSplitter;
    }

    public List<String> getWords(String wordString) {
        List<String> wordList = decoratedWordSplitter.getWords(wordString);
        for (int i = 0; i < wordList.size();) {
	    String word = wordList.get(i);
            if (word.isEmpty() || Character.isUpperCase(word.charAt(0))) {
                wordList.remove(i);
            } else {
	    	++i;
	    }
        }
        return wordList;
    }
}

class Main {   
    public static void main(String[] args) {
        WordSplitter wordSplitter = new PropperNamesFilteringSplitter(new ShortWordsFilteringSplitter(new PureWordSplitter()));
        List<String> result = wordSplitter.getWords("no hi Afrika yes ambitious come Ukraine Ua");
        for (String word : result) {
            System.out.print(word + " ");
        } 
    }
}

Зауваження і коментарі

  • Хоча об'єкт-декоратор може додавати свою функціональність до або після функціональності основного об'єкта, ланцюжок створюваних об'єктів завжди повинен закінчуватися об'єктом класу ConcreteComponent.
  • Базові класи мови Java широко використовують шаблон Декоратор для організації обробки операцій введення-виведення.

Посилання

  • Gamma, Erich; Helm, Richard; Johnson, Ralph; Vlissides, John (1994). Design Patterns: Elements of Reusable Object-Oriented Software (вид. [1]). Addison–Wesley. с. 395. {{cite book}}: Зовнішнє посилання в |edition= (довідка)(англ.)
  • Alan Shallowey, James R. Trott (2004). Design Patterns Explained: A New Perspective on Object-Oriented Design (PDF).(англ.)
  • Decorator design pattern [Архівовано 14 жовтня 2007 у Wayback Machine.]
  • Декоратор (Decorator) - Дизайн-патерни — просто, як двері
  • Чим відрізняється декоратор від адаптера? (І про фасад) [Архівовано 22 грудня 2014 у Wayback Machine.] - Блог одного кібера
Prefix: a b c d e f g h i j k l m n o p q r s t u v w x y z 0 1 2 3 4 5 6 7 8 9

Portal di Ensiklopedia Dunia

Kembali kehalaman sebelumnya