Абстрактный фабричный узор
Шаблон абстрактной фабрики в разработке программного обеспечения — это шаблон проектирования, который обеспечивает способ создания семейств связанных объектов без навязывания их конкретных классов путем инкапсуляции группы отдельных фабрик , имеющих общую тему, без указания их конкретных классов. [1] В соответствии с этим шаблоном компонент клиентского программного обеспечения создает конкретную реализацию абстрактной фабрики, а затем использует общий интерфейс фабрики для создания конкретных объектов , входящих в семейство. Клиент не знает , какие конкретные объекты он получает от каждой из этих внутренних фабрик, поскольку использует только общие интерфейсы их продуктов. [1] Этот шаблон отделяет детали реализации набора объектов от их общего использования и опирается на композицию объектов, поскольку создание объектов реализуется в методах, представленных в интерфейсе фабрики. [2]
Использование этого шаблона позволяет использовать взаимозаменяемые конкретные реализации без изменения кода, который их использует, даже во время выполнения . Однако использование этого шаблона, как и других подобных шаблонов проектирования , может привести к ненужной сложности и дополнительной работе при первоначальном написании кода. Кроме того, более высокие уровни разделения и абстракции могут привести к тому, что системы будет сложнее отлаживать и обслуживать.
Обзор
[ редактировать ]Шаблон проектирования «Абстрактная фабрика» — один из 23 шаблонов, описанных в книге «Шаблоны проектирования» 1994 года . Его можно использовать для решения таких проблем, как: [3]
- Как приложение может быть независимым от того, как создаются его объекты?
- Как класс может быть независимым от того, как создаются необходимые ему объекты?
- Как можно создавать семейства связанных или зависимых объектов?
Создание объектов непосредственно внутри класса, которому эти объекты требуются, является негибким. Это привязывает класс к определенным объектам и делает невозможным последующее изменение экземпляра без изменения класса. Это предотвращает повторное использование класса, если требуются другие объекты, и затрудняет тестирование класса, поскольку реальные объекты нельзя заменить фиктивными объектами.
Фабрика — это место конкретного класса в коде, в котором создаются объекты . Реализация шаблона призвана изолировать создание объектов от их использования и создать семейства связанных объектов независимо от их конкретных классов. [2] Это позволяет новые производные типы вводить без изменений в коде, использующем базовый класс .
В шаблоне описано, как решить такие проблемы:
- Инкапсулируйте создание объекта в отдельный (фабричный) объект, определив и реализовав интерфейс для создания объектов.
- Делегируйте создание объекта фабричному объекту вместо создания объектов напрямую.
Это делает класс независимым от того, как создаются его объекты. Класс может быть настроен с использованием фабричного объекта, который он использует для создания объектов, и фабричный объект можно обменивать во время выполнения.
Определение
[ редактировать ]Шаблоны проектирования описывают шаблон абстрактной фабрики как «интерфейс для создания семейств связанных или зависимых объектов без указания их конкретных классов». [4]
Использование
[ редактировать ]Фабрика определяет конкретный тип создаваемого объекта, и именно здесь объект фактически создается. Однако фабрика возвращает лишь ссылку (в Java, например, оператором ) new или указатель абстрактного типа на созданный конкретный объект.
Это изолирует клиентский код от создания объекта , поскольку клиенты запрашивают, чтобы фабричный объект создал объект желаемого абстрактного типа и вернул на этот объект абстрактный указатель. [5]
Примером является абстрактный фабричный класс. DocumentCreator
который предоставляет интерфейсы для создания ряда продуктов (например, createLetter()
и createResume()
). Система будет иметь любое количество производных конкретных версий DocumentCreator
класс, такой как FancyDocumentCreator
или ModernDocumentCreator
, каждый из которых имеет разную реализацию createLetter()
и createResume()
который создаст соответствующие объекты, такие как FancyLetter
или ModernResume
. Каждый из этих продуктов является производным от простого абстрактного класса, такого как Letter
или Resume
о чем знает клиент. Клиентский код получит экземпляр соответствующий DocumentCreator
и вызвать его фабричные методы . Каждый из получившихся объектов будет создан из одного и того же DocumentCreator
реализации и будут иметь общую тему. Клиенту нужно будет только знать, как обращаться с абстрактным Letter
или Resume
класс, а не конкретная версия, созданная бетонным заводом.
Поскольку фабрика возвращает только ссылку или указатель на абстрактный тип, клиентский код, запросивший объект у фабрики, не знает и не обременен фактическим конкретным типом созданного объекта. Однако абстрактная фабрика знает тип конкретного объекта (и, следовательно, конкретной фабрики). Например, фабрика может прочитать тип объекта из файла конфигурации. Клиенту не нужно указывать тип, поскольку тип уже указан в файле конфигурации. В частности, это означает:
- Клиентский код не знает конкретного типа и не требует включения каких-либо заголовочных файлов или классов объявлений , связанных с ним. Клиентский код имеет дело только с абстрактным типом. Объекты конкретного типа действительно создаются фабрикой, но клиентский код обращается к таким объектам только через их абстрактные интерфейсы . [6]
- Добавление новых конкретных типов выполняется путем изменения клиентского кода для использования другой фабрики, причем такая модификация обычно представляет собой одну строку в одном файле. Затем другая фабрика создает объекты другого конкретного типа, но по-прежнему возвращает указатель того же абстрактного типа, что и раньше, таким образом изолируя клиентский код от изменений. Это значительно проще, чем изменять клиентский код для создания экземпляра нового типа. Для этого потребуется изменить каждое место в коде, где создается новый объект, а также убедиться, что все такие места кода содержат информацию о новом конкретном типе, например, путем включения файла заголовка конкретного класса. Если все фабричные объекты хранятся глобально в одном одноэлементном объекте и весь клиентский код проходит через одиночный объект для доступа к соответствующей фабрике для создания объекта, то изменить фабрику так же просто, как изменить одноэлементный объект. [6]
Структура
[ редактировать ]UML-диаграмма
[ редактировать ]На приведенной выше UML классов диаграмме
тот Client
класс, который требует ProductA
и ProductB
объекты не создают экземпляры ProductA1
и ProductB1
занятия напрямую. Вместо этого Client
относится к AbstractFactory
интерфейс для создания объектов, что делает Client
независимо от того, как создаются объекты (какие конкретные классы создаются). Factory1
класс реализует AbstractFactory
интерфейс путем создания экземпляра ProductA1
и ProductB1
занятия.
Диаграмма UML последовательности показывает взаимодействие во время выполнения. Client
вызовы объектов createProductA()
на Factory1
объект, который создает и возвращает ProductA1
объект. После этого Client
звонки createProductB()
на Factory1
, который создает и возвращает ProductB1
объект.
Варианты
[ редактировать ]Исходная структура шаблона абстрактной фабрики, определенная в 1994 году в Design Patterns , основана на абстрактных классах для абстрактной фабрики и создаваемых абстрактных продуктов. Конкретные фабрики и продукты — это классы, которые специализируют абстрактные классы с помощью наследования. [4]
Более поздняя структура шаблона основана на интерфейсах, определяющих абстрактную фабрику и создаваемые абстрактные продукты. В этом проекте используется встроенная поддержка интерфейсов и протоколов основных языков программирования, чтобы избежать наследования. В этом случае конкретные фабрики и продукты представляют собой классы, реализующие интерфейс путем его реализации. [1]
Пример
[ редактировать ]Эта реализация C++11 основана на реализации до C++98, описанной в книге.
#include <iostream>
enum Direction {North, South, East, West};
class MapSite {
public:
virtual void enter() = 0;
virtual ~MapSite() = default;
};
class Room : public MapSite {
public:
Room() :roomNumber(0) {}
Room(int n) :roomNumber(n) {}
void setSide(Direction d, MapSite* ms) {
std::cout << "Room::setSide " << d << ' ' << ms << '\n';
}
virtual void enter() {}
Room(const Room&) = delete; // rule of three
Room& operator=(const Room&) = delete;
private:
int roomNumber;
};
class Wall : public MapSite {
public:
Wall() {}
virtual void enter() {}
};
class Door : public MapSite {
public:
Door(Room* r1 = nullptr, Room* r2 = nullptr)
:room1(r1), room2(r2) {}
virtual void enter() {}
Door(const Door&) = delete; // rule of three
Door& operator=(const Door&) = delete;
private:
Room* room1;
Room* room2;
};
class Maze {
public:
void addRoom(Room* r) {
std::cout << "Maze::addRoom " << r << '\n';
}
Room* roomNo(int) const {
return nullptr;
}
};
class MazeFactory {
public:
MazeFactory() = default;
virtual ~MazeFactory() = default;
virtual Maze* makeMaze() const {
return new Maze;
}
virtual Wall* makeWall() const {
return new Wall;
}
virtual Room* makeRoom(int n) const {
return new Room(n);
}
virtual Door* makeDoor(Room* r1, Room* r2) const {
return new Door(r1, r2);
}
};
// If createMaze is passed an object as a parameter to use to create rooms, walls, and doors, then you can change the classes of rooms, walls, and doors by passing a different parameter. This is an example of the Abstract Factory (99) pattern.
class MazeGame {
public:
Maze* createMaze(MazeFactory& factory) {
Maze* aMaze = factory.makeMaze();
Room* r1 = factory.makeRoom(1);
Room* r2 = factory.makeRoom(2);
Door* aDoor = factory.makeDoor(r1, r2);
aMaze->addRoom(r1);
aMaze->addRoom(r2);
r1->setSide(North, factory.makeWall());
r1->setSide(East, aDoor);
r1->setSide(South, factory.makeWall());
r1->setSide(West, factory.makeWall());
r2->setSide(North, factory.makeWall());
r2->setSide(East, factory.makeWall());
r2->setSide(South, factory.makeWall());
r2->setSide(West, aDoor);
return aMaze;
}
};
int main() {
MazeGame game;
MazeFactory factory;
game.createMaze(factory);
}
Вывод программы:
Maze::addRoom 0x1317ed0
Maze::addRoom 0x1317ef0
Room::setSide 0 0x1318340
Room::setSide 2 0x1317f10
Room::setSide 1 0x1318360
Room::setSide 3 0x1318380
Room::setSide 0 0x13183a0
Room::setSide 2 0x13183c0
Room::setSide 1 0x13183e0
Room::setSide 3 0x1317f10
См. также
[ редактировать ]- Класс бетона
- Шаблон фабричного метода
- Создание объекта
- Шаблон проектирования программного обеспечения
Ссылки
[ редактировать ]- ^ Jump up to: а б с Фриман, Эрик; Робсон, Элизабет; Сьерра, Кэти; Бейтс, Берт (2004). Хендриксон, Майк; Лукидес, Майк (ред.). Шаблоны проектирования Head First (мягкая обложка) . Том. 1. О'РЕЙЛИ. п. 156. ИСБН 978-0-596-00712-6 . Проверено 12 сентября 2012 г.
- ^ Jump up to: а б Фриман, Эрик; Робсон, Элизабет; Сьерра, Кэти; Бейтс, Берт (2004). Хендриксон, Майк; Лукидес, Майк (ред.). Шаблоны проектирования Head First (мягкая обложка) . Том. 1. О'РЕЙЛИ. п. 162. ИСБН 978-0-596-00712-6 . Проверено 12 сентября 2012 г.
- ^ «Шаблон проектирования «Абстрактная фабрика» — проблема, решение и применимость» . w3sDesign.com . Проверено 11 августа 2017 г.
- ^ Jump up to: а б Гамма, Эрих; Ричард Хелм; Ральф Джонсон; Джон М. Влиссидес (23 октября 2009 г.). «Шаблоны проектирования: абстрактная фабрика» . ИнформИТ. Архивировано из оригинала 16 мая 2012 г. Проверено 16 мая 2012 г.
Создание объектов: Абстрактная фабрика: Цель: предоставить интерфейс для создания семейств связанных или зависимых объектов без указания их конкретных классов.
{{cite web}}
: CS1 maint: bot: исходный статус URL неизвестен ( ссылка ) - ^ Винеман, Дэвид (23 октября 2009 г.). «Предметный дизайн для недоумевающих» . Проект Кодекса. Архивировано из оригинала 21 февраля 2011 г. Проверено 16 мая 2012 г.
Фабрика изолирует клиента от изменений в продукте или способе его создания и может обеспечить эту изоляцию между объектами, полученными из самых разных абстрактных интерфейсов.
{{cite web}}
: CS1 maint: bot: исходный статус URL неизвестен ( ссылка ) - ^ Jump up to: а б «Абстрактная фабрика: реализация» . OODesign.com . Проверено 16 мая 2012 г.
- ^ «Шаблон проектирования «Абстрактная фабрика» — структура и сотрудничество» . w3sDesign.com . Проверено 12 августа 2017 г.
Внешние ссылки
[ редактировать ]- СМИ, связанные с Абстрактной фабрикой, на Викискладе?
- Абстрактная фабрика Пример реализации абстрактной фабрики