{
protected:
Car* mCar;
public:
CarDecorator(Car* car): mCar(car) {};
void setCar(Car* car) {mCar = car;}
...
virtual float getCost() {return mCar->getCost();}
virtual void addEngine(Engine* engine) {mCar->addEngine(engine);}
...
};
Как вы видите, базовый декоратор просто перенаправляет вызовы в объект mCar, который хранит, и кажется бесполезным. Но вся суть в классах, порожденных от него (см. листинг 4).
Листинг 4
class CarDecoratorLogger: public CarDecorator
{
...
virtual float getCost()
{
float retValue = CarDecorator::getCost();
LOG_TO_FILE(«getCost for %s returned %d», mCar->getDescription(), retValue);
return retValue;
}
virtual void addEngine(Engine* engine)
{
LOG_TO_FILE(«addEngine(%s) for %s», engine->getDescription(), mCar->getDescription());
CarDecorator::addEngine(engine);
}
...
};
Кроме добавления новых операций, декоратор часто используется для хранения состояния объекта или для добавления понятия состояния объектам, у которых его нет внутри. Например, мы можем отслеживать продвижение сборки автомобиля (см. листинг 5).
Листинг 5
class CarDecoratorProgressTracker: public CarDecorator
{
bool mHaveDoors;
bool mHaveEngine;
bool mHaveWheels;
...
virtual void addEngine(Engine* engine)
{
if (engine)
mHaveEngine = true;
CarDecorator::addEngine(engine);
}
virtual void addDoors(Door* doors, int doorsCount)
{
if (doorsCount && doors)
mHaveDoors = true;
CarDecorator::addDoors(doors, doorsCount);
}
...
virtual bool readyToMove()
{
return (mHaveDoors && mHaveEngine && mHaveWheels);
};
};
Теперь можно изменить поведение объекта Car, если везде хранить указатель не на него, а на CarDecorator. Создавая нужный CarDecorator, мы можем менять поведение всей иерархии объектов Car, не изменяя код в самих этих объектах и не создавая новые иерархии объектов, порожденных от Car.
«Фасад»
Шаблон «Фасад» (Facade) позволяет скрыть сложность системы, направив все возможные внешние вызовы через один объект-фасад, который просто делегирует эти вызовы соответствующим объектам системы.
Известно, что одна из основных характеристик сложности кода – это степень связанности (coupling), т. е. мера, определяющая, насколько жестко один элемент кода связан с другими элементами либо каким количеством данных о других элементах он обладает.
Элемент с низкой степенью связанности (слабосвязанный, low coupling) зависит от не слишком большого числа других элементов. Выражение «слишком много» зависит от контекста, и в каждом проекте это свое число.
Класс с высокой степенью связанности (или жестко связанный) зависит от множества других классов. Наличие таких классов нежелательно, поскольку оно приводит к возникновению многих проблем, – например, изменения в связанных классах приводят к локальным изменениям в данном классе, затрудняется также понимание каждого класса в отдельности и усложняется повторное использование, поскольку для этого требуется дополнительный анализ классов, с которыми связан данный класс.
Именно для уменьшения степени связанности с целой подсистемой и применяется паттерн «Фасад». Если у вас есть подсистема, которая часто меняется, и из-за этого вы хотите оградить клиентов этой системы от знания деталей ее реализации (уменьшить связность), то логичное решение – создать специальный класс-фасад, который будет предоставлять весь набор функций данной подсистемы. Таким образом, все клиенты будут общаться с подсистемой только посредством этого класса-фасада.
Например, предположим, что наш код для работы с автомобилями, описанный выше, – это только одна из подсистем огромной программы. И мы хотим оградить остальные подсистемы от частых изменений этой подсистемы для работы с автомобилями, так как мы знаем, что она часто меняется и будет меняться. Применим шаблон «Фасад» и создадим специальный класс CarSystemFacade (см. листинг 6).
Листинг 6
const int InvalidCarId = -1;
const int InvalidEngineId = -1;
...
class CarSystemFacade
{
...
typedef int CarId;