Журнал PC Magazine/Russian Edition 01/2009
Шрифт:
class DocumentWriter
{
public: virtual void writeDoc(const Document& doc, FILE& file) = 0;
};
class SaveDocument
{
protected:
virtual DocumentFileWriter* createDocumentWriter const = 0;
public:
void save(const char* fileName)
{
FILE file = openFile(fileName);
// Фабричный метод
DocumentFileWriter* writer = createDocumentWriter;
writer->writeDoc;
closeFile(file);
}
};
Обратите внимание, что теперь не надо менять базовые классы для добавления любого нового формата выходного файла. Нужно только создать два простых дополнительных класса, например для вывода в XML (см. листинг 1).
Листинг 1
class DocumentWriterXml: public DocumentWriter
{
public:
virtual void writeDoc(const Document& doc, FILE& file)
{
//
}
};
class SaveDocumentXml: public SaveDocument
{
protected:
virtual DocumentFileWriter* createDocumentWriter const {return new DocumentWriterXml;}
};
Главный недостаток этого шаблона – необходимость создания двух дополнительных классов при добавлении новой функции.
«Абстрактная фабрика»
Шаблон Abstract Factory предоставляет интерфейс для создания семейств связанных или зависимых объектов, позволяя не указывать их конкретные классы. Обычно он применяется, когда нужно создавать составные объекты из конкретных частей (объектов). Классический пример – автомобиль. Если есть классы для разных частей автомобиля, то конкретный автомобиль будет состоять из множества таких частей, причем в каждой марке автомобиля будут конкретные части-детали. Для окончательной сборки автомобиля можно использовать «Абстрактную фабрику», классы-потомки которой будут создавать конкретные автомобили.
class AbstractCarFactory
{
public:
virtual Car* createCar = 0;
virtual Engine* createEngine = 0;
virtual Wheel* createWheel = 0;
...
};
Нам также понадобятся иерархии классов для каждой из частей автомобиля (см. листинг 2).
Листинг 2
class Engine
{
public:
virtual void setSpeedLimit(int kmph);
virtual void addOil(float amount);
...
};
class Wheel
{
float mRadius;
public:
Wheel(float radius): mRadius(radius) {};
virtual float getRadius const {return mRadius;}
...
};
class Car
{
public:
virtual const char* getName const = 0;
virtual void setEngine(Engine* engine);
virtual void setWheels(Wheel* FR, Wheel* FL, Wheel* BR, Wheel* BL);
virtual void setFrontWheels(Wheel* FR, Wheel* FL);
virtual void setBackWheels(Wheel* BR, Wheel* BL);
...
};
Процедура сборки итогового объекта приводится в листинге 3.
Листинг 3
Car* createCar(AbstractCarFactory* carFactory)
{
Car* car = carFactory->createCar;
Engine* engine = carFactory->createEngine;
car->setEngine(engine);
car->setWheels(carFactory->createWheel, carFactory->createWheel, сarFactory->createWheel, carFactory->createWheel);
// дальше может идти сколько угодно сложный код для «сборки» готового автомобиля из частей
return car;
}
Для добавления конкретных моделей автомобилей нужно просто создать новый класс, породив его от AbstractCarFactory. Задача этого класса – создать конкретные детали автомобиля. Понадобится также создать классы этих конкретных деталей, если их еще нет.
class BMW5CarFactory: public AbstractCarFactory
{
рublic:
virtual Car* createCar {return new CarBMW5;}
virtual Engine* createEngine {return new EngineBMW5;}
virtual Wheel* createWheel {return new WheelNokian(16);}
...
};
class CarBMW5: public Car
{
public:
virtual const char* getName const {return
«BMW5»;}...
};
Главный недостаток этого паттерна состоит в том, что со временем становится труднее вносить изменения в структуру классов. Например, если у нас уже имеется 100 конкретных моделей автомобилей, а мы решили добавить в AbstractCarFactory новую функцию, создающую фары, то нам придется изменить все 100 классов-потомков, создающих конкретные модели.
«Строитель»
Суть этого паттерна – отделить процесс создания сложного объекта от его представления. Таким образом, можно получать различные представления объекта, используя один и тот же технологический процесс его создания. На первый взгляд этот паттерн кажется похожим на «Абстрактную фабрику», но отличия есть. Можно сказать, что «Абстрактная фабрика» концентрируется на том, что создается, а «Строитель» на том, как создается.
Рассмотрим тот же самый пример с автомобилями. Для создания автомобиля нам теперь понадобится специальный класс CarBuilder.
class CarBuilder
{
Car* mCar;
virtual Car* _createNewCar const {return Car;}
public:
Car* getCar const {return mCar;}
void createNewCar {mCar = _createNewCar;}
virtual void addEngine = 0;
virtual void addWheels = 0;
...
};
Необходимо также создать класс Director, управленец, в котором будет реализован алгоритм сборки автомобиля. Этот класс не создает конкретных автомобилей, а использует для этого CarBuilder. Его задача – воплотить общий алгоритм конструирования автомобиля, тогда как детали должны быть реализованы в потомках класса CarBuilder. Таким образом достигается важная задача: общий алгоритм создания автомобиля остается неизменным, но при этом конкретный автомобиль может создаваться по сколь угодно сложной схеме, так как CarDirector делегирует CarBuilder всю работу по созданию конкретного автомобиля.
class CarDirector
{
сarBuilder* mBuilder;
public:
void setCarBuilder(CarBuilder* builder)
{
mBuilder = builder;
}
Car* getCar const
{
return mBuilder->getCar;
}
void constructCar
{
mBuilder-createNewCar;
mBuilder->addEngine;
mBuilder->addWheels;
...
}
};
Итак, теперь у нас есть класс CarDirector, который задает правила создания автомобилей, и класс CarBuilder, потомки которого создают конкретные детали автомобиля по правилам, описанным в CarDirector. Например, для сборки BMW5 нам понадобится новый класс, порожденный от CarBuilder (см. листинг 4).
Листинг 4
class BMW5CarBuilder: public CarBuilder
{
public:
virtual void addEngine
{
Engine* engine = new EngineBMW5;
engine->setSpeedLimit(250);
engine->addOil(10);
getCar->setEngine(engine);
}
virtual void addWheels
{
getCar->setFrontWheels(new WheelNokian(16), new WheelNokian(16));
getCar->setBackWheels(new WheelBridestone(16), new WheelBridestone(16));
}
...
};
Обратим внимание на отличие в конструировании объектов от паттерна «Абстрактная фабрика». Класс BMW5CarBuilder имеет больше возможностей конструирования, чем BMW5CarFactory, поскольку он нацелен на процесс сборки. А BMW5CarFactory просто создает детали и не может влиять на процесс конструирования. В этом основное различие между ними.
Главная проблема этого паттерна та же, что и у «Абстрактной фабрики» – сложно вносить изменения в систему, если она уже наполнена конкретными классами, так как приходится изменять их все.