Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Итог_Пособие C++.doc
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
2.03 Mб
Скачать

6.4 Шаблон "фабричный метод" (Factory method)

Шаблон "фабричный метод" решает задачу создания объектов в программе, при этом тип создаваемого объекта не указывается с помощью оператора new, что делает систему гибкой к изменению типов создаваемых объектов. Данный шаблон удобно применять в том случае, когда заранее неизвестно, какой тип объектов будет создан, а также в случае, если новые типы объектов будут добавляться по мере разработки и сопровождения программы.

Фабричный метод может быть реализован двумя способами: с помощью создания отдельного класса-фабрики, которая будет отвечать за создание конкретного типа объектов, либо с помощью создания в базовом классе статического метода, который, принимая идентификатор типа объекта в виде перечисляемого типа или строки, будет возвращать указатель на экземпляр созданного класса. Статический метод во втором случае также называют обобщенным конструктором.

Рассмотрим пример кода для варианта фабричного метода с обобщенным конструктором. В следующем фрагменте создается базовый класс Shape, содержащий обобщенный конструктор − метод createShape() и подклассы Circle, Rect, Triangle, переопределяющие виртуальную функцию draw(). Идентификатор создаваемого объекта передается в обобщенный конструктор в виде строки с именем класса, который необходимо создать.

// Пример 6.1.1 - шаблон "фабричный метод" на основе обобщенного

// конструктора.

#include <iostream>

#include <vector>

#include <string>

// Базовый класс иерархии

class Shape

{

public:

virtual ~Shape() {}

virtual void draw() = 0;

// Обобщенный конструктор. В качестве параметра передается

// имя типа объекта.

static Shape* createShape(const std::string& shapeType);

};

class Circle : public Shape

{

public:

virtual ~Circle() {}

virtual void draw() { std::cout << "Circle" << std::endl; }

};

class Rect : public Shape

{

public:

virtual ~Rect() {}

virtual void draw() { std::cout << "Rect" << std::endl; }

};

class Triangle : public Shape

{

public:

virtual ~Triangle() {}

virtual void draw() { std::cout << "Triangle" << std::endl; }

};

// Реализация обобщенного конструктора. В зависимости от переданной

// строки создается экземпляр нужного класса. После введения нового

// класса в иерархию потребуется дополнить обобщенный конструктор.

Shape* Shape::createShape(const std::string& shapeType)

{

if("Circle" == shapeType) return new Circle();

if("Rect" == shapeType) return new Rect();

if("Triangle" == shapeType) return new Triangle();

return 0;

}

Далее приведен пример создания объектов с помощью фабричного метода, основанного на обобщенном конструкторе. В функции test_1() выполняется заполнение вектора объектами одного из трех типов, при этом тип объекта указывается непосредственно в коде. В функции test_2() выполняется аналогичное создание объектов с помощью фабричного метода, при этом тип объекта указывается в виде строки.

// Пример 6.1.2 - шаблон "фабричный метод".

// Тестирование, создание объектов без применения фабричного

// метода - test_1() и с применением фабричного метода - test_2()

void test_1()

{

std::vector<Shape*> sv;

for(int i = 0; i != 10; ++i)

{

int r = rand() % 3;

// Тип создаваемых объектов точно указан

if(0 == r) sv.push_back(new Circle());

if(1 == r) sv.push_back(new Rect());

if(2 == r) sv.push_back(new Triangle());

}

for(int i = 0; i != 10; ++i)

{

sv[i]->draw();

}

}

void test_2()

{

std::vector<Shape*> sv;

for(int i = 0; i != 10; ++i)

{

int r = rand() % 3;

// Тип создаваемых объектов задан в виде строки

if(0 == r) sv.push_back(Shape::createShape("Circle"));

if(1 == r) sv.push_back(Shape::createShape("Rect"));

if(2 == r) sv.push_back(Shape::createShape("Triangle"));

}

for(int i = 0; i != 10; ++i)

{

sv[i]->draw();

}

}

int main()

{

test_1();

std::cout << std::endl;

test_2();

system("pause");

}

В данном случае строка с типом объекта указывается при вызове обобщенного конструктора, однако она может быть введена пользователем или прочитана из файла. Таким образом, исключается жесткая зависимость от типа создаваемого объекта.

Результат работы программы следующий:

Triangle

Triangle

Rect

Rect

Triangle

Rect

Circle

Circle

Rect

Triangle

Triangle

Triangle

Rect

Circle

Rect

Triangle

Rect

Triangle

Circle

Circle

Рассмотренная реализация фабричного метода имеет свои недостатки:

1. Обобщенный конструктор содержит большое количество однотипных конструкций сравнения идентификаторов.

2. В статическом методе createShape() прописываются все имена подклассов, которые могут быть созданы обобщенным конструктором. То есть, в базовом классе есть информация обо всех его подклассах. Такой подход искажает принцип наследования в объектно-ориентированном программировании и делает невозможным независимое создание новых типов объектов без изменения базового класса.

Вариант фабричного метода с отдельным классом-фабрикой не имеет указанных недостатков, однако это достигается за счет большего объема программного кода.

Следующий фрагмент кода содержит базовый класс Shape и три подкласса Circle, Rect, Triangle, а также базовый класс фабрики ShapeFactory и три подкласса для создания соответствующих подклассов класса Shape. Класс ShapeFactory имеет виртуальный метод createShape().

// Пример 6.2.1 - шаблон "фабричный метод" на основе фабрик объектов.

#include <iostream>

#include <vector>

#include <string>

#include <map>

// Базовый класс иерархии объектов

class Shape

{

public:

virtual ~Shape() {}

virtual void draw() = 0;

};

class Circle : public Shape

{

public:

virtual ~Circle() {}

virtual void draw() { std::cout << "Circle" << std::endl; }

};

class Rect : public Shape

{

public:

virtual ~Rect() {}

virtual void draw() { std::cout << "Rect" << std::endl; }

};

class Triangle : public Shape

{

public:

virtual ~Triangle() {}

virtual void draw() { std::cout << "Triangle" << std::endl; }

};

// Базовый класс фабрики объектов

class ShapeFactory

{

public:

virtual ~ShapeFactory() {}

virtual Shape* createShape() = 0;

};

class CircleFactory : public ShapeFactory

{

public:

virtual Shape* createShape() { return new Circle(); }

};

class RectFactory : public ShapeFactory

{

public:

virtual Shape* createShape() { return new Rect(); }

};

class TriangleFactory : public ShapeFactory

{

public:

virtual Shape* createShape() { return new Triangle(); }

};

Класс ShapeManager может быть использован для удобства управления объектами фабрик и обеспечения централизованного доступа к созданию объектов. Данный класс содержит два метода − addFactory() и createShape(). Метод addFactory() регистрирует фабрику, присваивая ей определенное имя, метод createShape() получает имя объекта, который нужно создать, ищет фабрику с таким именем и делегирует ей запрос на создание.

// Пример 6.2.2 - шаблон "фабричный метод".

// Вспомогательный класс ShapeManager, позволяющий хранить экземпляры

// фабрик объектов, а также делегировать запросы на создание

// объектов фабрикам.

class ShapeManager

{

// Ассоциативный массив с фабриками. Тип фабрики и тип объектов

// определяется с помощью строки.

std::map<std::string, ShapeFactory*> mFactoryMap;

public:

~ShapeManager()

{

// Удаление фабрик.

for(std::map<std::string, ShapeFactory*>::iterator it =

mFactoryMap.begin();

it != mFactoryMap.end();

++it)

{

delete it->second;

}

mFactoryMap.clear();

}

void addFactory(const std::string& name, ShapeFactory* factory)

{

// Фабрика сохраняется в ассоциативном массиве

// под заданным именем

mFactoryMap.insert(std::make_pair(name, factory));

}

Shape* createShape(const std::string& shapeType)

{

// Делегирования запроса на создание объекта фабрике с

// указанным именем.

return mFactoryMap[shapeType]->createShape();

}

};

В следующем фрагменте кода приведен новый класс объектов MyShape и фабрики для создания экземпляров данного класса, а также функция test_2() для демонстрации работы фабричного метода. Код метода test_1() аналогичен коду одноименного метода из примера с обобщенным конструктором.

// Пример 6.2.3 - шаблон "фабричный метод"

// Новый класс фигур

class MyShape : public Shape

{

public:

virtual ~MyShape() {}

virtual void draw() { std::cout << "MyShape" << std::endl; }

};

// Фабрика для создания новых фигур

class MyShapeFactory : public ShapeFactory

{

public:

virtual Shape* createShape() { return new MyShape(); }

};

void test_2()

{

ShapeManager shapeManager;

// Регистрация фабрик в менеджере

shapeManager.addFactory("Circle", new CircleFactory());

shapeManager.addFactory("Rect", new RectFactory());

shapeManager.addFactory("Triangle", new TriangleFactory());

// Добавление новой фабрики

shapeManager.addFactory("MyShape", new MyShapeFactory());

std::vector<Shape*> sv;

for(int i = 0; i != 15; ++i)

{

int r = rand() % 4;

// Процесс создания объектов похож на вариант с обобщенным

// конструктором, однако, в данном примере объекты создаются

// в конкретных фабриках, а запросы на создание объектов

// направляются менеджеру.

if(0 == r) sv.push_back(shapeManager.createShape("Circle"));

if(1 == r) sv.push_back(shapeManager.createShape("Rect"));

if(2 == r) sv.push_back(shapeManager.createShape("Triangle"));

if(3 == r) sv.push_back(shapeManager.createShape("MyShape"));

}

for(int i = 0; i != 15; ++i)

{

sv[i]->draw();

}

}

int main()

{

test_1();

std::cout << std::endl;

test_2();

system("pause");

}

Результат работы программы следующий:

Triangle

Triangle

Rect

Rect

Triangle

Rect

Circle

Circle

Rect

Triangle

Rect

Rect

Rect

MyShape

Rect

MyShape

MyShape

Triangle

MyShape

Circle

MyShape

Circle

Triangle

Rect

Circle

Рассмотренный вариант фабричного метода позволяет создавать объекты без жесткого указания типа объектов, а также позволяет расширять систему за счет определения новых объектов за пределами основной программы − например, в динамической библиотеке. Для создания нового типа объектов достаточно определить сам класс объектов и класс-фабрику.

Рассмотрим в качестве дополнительного упражнения модификацию предыдущего варианта фабричного метод с шаблонным классом − фабрикой. Шаблонная фабрика позволит избежать написания однотипного кода классов фабрик.

// Пример 6.3 - шаблон "фабричный метод" на основе фабрики объектов.

// Фабрика реализована через механизм шаблонов (template) языка С++

#include <iostream>

#include <vector>

#include <string>

#include <map>

class Shape

{

public:

virtual ~Shape() {}

virtual void draw() = 0;

};

class Circle : public Shape

{

public:

virtual ~Circle() {}

virtual void draw() { std::cout << "Circle" << std::endl; }

};

class Rect : public Shape

{

public:

virtual ~Rect() {}

virtual void draw() { std::cout << "Rect" << std::endl; }

};

class Triangle : public Shape

{

public:

virtual ~Triangle() {}

virtual void draw() { std::cout << "Triangle" << std::endl; }

};

class ShapeFactoryInterface

{

public:

virtual ~ShapeFactoryInterface() {}

virtual Shape* createShape() = 0;

};

template<class TShape>

class ShapeFactory : public ShapeFactoryInterface

{

public:

virtual ~ShapeFactory() {}

virtual Shape* createShape() { return new TShape(); }

};

class ShapeManager

{

std::map<std::string, ShapeFactoryInterface*> mFactoryMap;

public:

~ShapeManager()

{

for(std::map<std::string,ShapeFactoryInterface*>::iterator it

= mFactoryMap.begin();

it != mFactoryMap.end();

++it)

{

delete it->second;

}

mFactoryMap.clear();

}

void addFactory(const std::string& name,

ShapeFactoryInterface* factory)

{

mFactoryMap.insert(std::make_pair(name, factory));

}

Shape* createShape(const std::string& shapeType)

{

return mFactoryMap[shapeType]->createShape();

}

};

void test_1()

{

std::vector<Shape*> sv;

for(int i = 0; i != 10; ++i)

{

int r = rand() % 3;

if(0 == r) sv.push_back(new Circle());

if(1 == r) sv.push_back(new Rect());

if(2 == r) sv.push_back(new Triangle());

}

for(int i = 0; i != 10; ++i)

{

sv[i]->draw();

}

}

class MyShape : public Shape

{

public:

virtual ~MyShape() {}

virtual void draw() { std::cout << "MyShape" << std::endl; }

};

void test_2()

{

ShapeManager shapeManager;

shapeManager.addFactory("Circle", new ShapeFactory<Circle>());

shapeManager.addFactory("Rect", new ShapeFactory<Rect>());

shapeManager.addFactory("Triangle", new ShapeFactory<Triangle>());

shapeManager.addFactory("MyShape", new ShapeFactory<MyShape>());

std::vector<Shape*> sv;

for(int i = 0; i != 15; ++i)

{

int r = rand() % 4;

if(0 == r) sv.push_back(shapeManager.createShape("Circle"));

if(1 == r) sv.push_back(shapeManager.createShape("Rect"));

if(2 == r)

sv.push_back(shapeManager.createShape("Triangle"));

if(3 == r) sv.push_back(shapeManager.createShape("MyShape"));

}

for(int i = 0; i != 15; ++i)

{

sv[i]->draw();

}

}

int main()

{

test_1();std::cout << std::endl;

test_2();

system("pause");

}

Результат работы программы следующий:

Triangle

Triangle

Rect

Rect

Triangle

Rect

Circle

Circle

Rect

Triangle

Rect

Rect

Rect

MyShape

Rect

MyShape

MyShape

Triangle

MyShape

Circle

MyShape

Circle

Triangle

Rect

Circle

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]