Шаг 9 - Декоратор. Decorator

Снова к нашей игре.

Пусть юнитам (CUnit) в нашей нужно бывает окружать себя простой рамкой. Хорошим решением будет создать новый подкласс - который будет окружать юнит рамкой (CBorderedUnit). А иногда нужна прокрутка, например если строение слишком большое, чтобы его отражать полностью. Тогда создаем подкласс - который реализует прокрутку (CScrollUnit). Ну, конечно, если понадобится прокрутка и простая рамка, то породим класс (CBorderedScrollUnit) - множественным наследованием. Или один от другого. Только это не решит проблемы, поскольку вот еще видел я в Visual... такую рамочку вокруг компонента, которая такая из восьми черных квадратиков, она еще позволяет изменять размер и положение (ах, Малевич, не поняли его в России, а какое влияние на мир оказал). Эта рамка нужна больше жизни. И теперь мы получаем еще пачку классов CDesing, CDesignBordered, CDesignBordered - и так далее.

В общем, решение неудачное.

А вот решение получше: Пусть всякие рамки компонетам добавляет другой компонент-контейнер, который все запросы к себе исправно перенаправляет лежащему в нем объекту, а от себя добавляет только минимальное дополнительное поведение. При этом контейнер видит базовый класс компонента, и если мы положим в контейнер потомок базового класса или другой похожий контейнер, то он не заметит подмены. Это как в дельфях Вы выкладываете на форму панель, на панель скроллер, на скроллер картинку. О такой феньке я рассказывал в "Стили и идиомы": "Шаг 7 - Интерфейсы. Интерфейсные указатели", там это называлось интерфейсным указателем, а здесь - декоратором; кроме того, требуется порождать компонент и его декоратор от одного базового класса, чтобы уже ИХ контейнер так же не замечал подмены.

// Общий предок с функцией отрисовки.
class CUnit
{
public:
	virtual void draw ();
};
// Какой-то конкретный юнит со своей отрисовкой.
class CSomeUnit : public CUnit
{
public:
	void draw ();
};
// базовый декоратор, перенаправляет вызовы
class CDecorator : public CUnit
{
public:
	CUnit* m_unit;
	// Все декораторы перенаправляют вызовы как минимум
	void draw () {m_unit->draw(); };
};
//Конкретный декоратор, добавляет собственное поведение
class CBorderDecorator : public CDecorator
{
	// вот оно, собственное поведение
	void drawBorder ();
public:
	void draw ()
	{
		// сначала вызывается общее поведение 
		CDecorator::draw();
		// а теперь частное.
		drawBorder ();
	};
};

Предыдущий Шаг | Следующий Шаг | Оглавление
Автор Albert Makhmutov - 07.07.2001