Читайте также:
|
|
protected:
virtual Product* CreateProduct(); private:
Product* _product;
Product* Creator::GetProduct () { if (.product == 0) {
_product = CreateProduct ();
return _product;
а использование шаблонов, чтобы не порождать подклассы. К сожалению, допустима ситуация, когда вам придется порождать подклассы только для того, чтобы создать подходящие объекты-продукты. В C++ этого можно избежать, предоставив шаблонный подкласс класса Creator, параметризованный классом Product:
class Creator { public:
virtual Product* CreateProduct () = 0; I.
template <class TheProduct>
class StandardCreator: public Creator {
public:
virtual Product* CreateProduct();
template <class TheProduct>
Product* StandardCreator<TheProduct>::CreateProduct () {
return new TheProduct; }
С помощью данного шаблона клиент передает только класс продукта, порождать подклассы от Creator не требуется:
class MyProduct: public Product { public:
MyProduct();
StandardCreator<MyProduct> myCreator;
а соглашения об именовании. На практике рекомендуется применять такие соглашения об именах, которые дают ясно понять, что вы пользуетесь фабричными методами. Например, каркас МасАрр на платформе Macintosh [App89] всегда объявляет абстрактную операцию, которая определяет фабричный метод, в виде Class* DoMakeClass (), где Class - это класс продукта.
Паттерн Factory Method
Пример кода
Функция CreateMaze строит и возвращает лабиринт. Одна из связанных с ней проблем состоит в том, что классы лабиринта, комнат, дверей и стен жестко «зашиты» в данной функции. Мы введем фабричные методы, которые позволят выбирать эти компоненты подклассам.
Сначала определим фабричные методы в игре MazeGame для создания объектов лабиринта, комнат, дверей и стен:
class MazeGame { public:
Maze* CreateMaze();
// фабричные методы:
virtual Maze* MakeMazeO const
{ return new Maze; } virtual Room* MakeRoom(int n) const
{ return new Room(n); } virtual Wall* MakeWalK) const
{ return new Wall; } virtual Door* MakeDoor(Room* rl, Room* r2) const
{ return new Door(rl, r2); }
Каждый фабричный метод возвращает один из компонентов лабиринта. Класс MazeGame предоставляет реализации по умолчанию, которые возвращают простейшие варианты лабиринта, комнаты, двери и стены.
Теперь мы можем переписать функцию CreateMaze с использованием этих фабричных методов:
Maze* MazeGame::CreateMaze () { Maze* aMaze = MakeMaze();
Room* rl = MakeRoom(l); Room* r2 = MakeRoom(2); Door* theDoor = MakeDoor(rl, r2);
aMaze->AddRoom(rl); aMaze->AddRoom(r2);
rl->SetSide (North, MakeWall()); rl->SetSide(East, theDoor); rl->SetSide (South, MakeWall()); rl->SetSide(West, MakeWall());
r2->SetSide (North, MakeWall()); r2->SetSide(East, MakeWall()); r2->SetSide (South, MakeWall()); r2->SetSide(West, theDoor);
Порождающие паттерны
return aMaze;
В играх могут порождаться различные подклассы MazeGame для специализации частей лабиринта. В этих подклассах допустимо переопределение некоторых или всех методов, от которых зависят разновидности продуктов. Например, в игре BombedMazeGame продукты Room и Wall могут быть переопределены так, чтобы возвращать комнату и стену с заложенной бомбой:
class BombedMazeGame: public MazeGame { public:
BombedMazeGame();
virtual Wall* MakeWall() const { return new BombedWall; }
virtual Room* MakeRoom(int n) const { return new RoomWithABomb(n); }
А в игре Enchant edMazeGame допустимо определить такие варианты:
class EnchantedMazeGame: public MazeGame { public:
EnchantedMazeGame();
virtual Room* MakeRoomdnt n) const
{ return new EnchantedRoom(n, CastSpell()); }
virtual Door* MakeDoor(Room* rl, Room* r2) const
{ return new DoorNeedingSpell(rl, r2); } protected:
Spell* CastSpell() const;
Известные применения
Фабричные методы в изобилии встречаются в инструментальных библиотеках и каркасах. Рассмотренный выше пример с документами - это типичное применение в каркасе МасАрр и библиотеке ЕТ++ [WGM88]. Пример с манипулятором заимствован из каркаса Unidraw.
Класс View в схеме модель/вид/контроллер из языка Smalltalk-80 имеет метод defaultController, который создает контроллер, и этот метод выглядит как фабричный [РагЭО]. Но подклассы View специфицируют класс своего контроллера по умолчанию, определяя метод def aultControllerClass, возвращающий класс, экземпляры которого создает defaultController. Таким образом, реальным фабричным методом является def aultControllerClass, то есть метод, который должен переопределяться в подклассах.
Более необычным является пример фабричного метода parserClass, тоже взятый из Smalltalk-80, который определяется поведением Behavior (суперкласс
Дата добавления: 2015-09-11; просмотров: 84 | Поможем написать вашу работу | Нарушение авторских прав |