Шаблон абстрактной фабрики в программной инженерии — это шаблон проектирования, который предоставляет способ создания семейств связанных объектов без навязывания их конкретных классов путем инкапсуляции группы отдельных фабрик , имеющих общую тему, без указания их конкретных классов. [1] Согласно этому шаблону, компонент клиентского программного обеспечения создает конкретную реализацию абстрактной фабрики, а затем использует общий интерфейс фабрики для создания конкретных объектов , которые являются частью семейства. Клиент не знает, какие конкретные объекты он получает от каждой из этих внутренних фабрик, поскольку он использует только общие интерфейсы их продуктов. [1] Этот шаблон отделяет детали реализации набора объектов от их общего использования и полагается на композицию объектов, поскольку создание объектов реализуется в методах, представленных в интерфейсе фабрики. [2]
Использование этого шаблона позволяет использовать взаимозаменяемые конкретные реализации без изменения кода, который их использует, даже во время выполнения . Однако использование этого шаблона, как и аналогичных шаблонов проектирования , может привести к ненужной сложности и дополнительной работе при первоначальном написании кода. Кроме того, более высокие уровни разделения и абстракции могут привести к системам, которые сложнее отлаживать и обслуживать.
Шаблон проектирования абстрактной фабрики — один из 23 шаблонов, описанных в книге Design Patterns 1994 года . Он может быть использован для решения таких проблем, как: [3]
Создание объектов непосредственно в классе, которому требуются объекты, негибко. Это привязывает класс к определенным объектам и делает невозможным изменение экземпляра позже без изменения класса. Это не позволяет повторно использовать класс, если требуются другие объекты, и затрудняет тестирование класса, поскольку реальные объекты нельзя заменить фиктивными объектами.
Фабрика — это местоположение конкретного класса в коде, в котором конструируются объекты . Реализация шаблона направлена на то, чтобы изолировать создание объектов от их использования и создавать семейства связанных объектов вне зависимости от их конкретных классов. [2] Это позволяет вводить новые производные типы без изменения кода, использующего базовый класс .
Шаблон описывает, как решать такие проблемы:
Это делает класс независимым от того, как создаются его объекты. Класс может быть сконфигурирован с помощью объекта фабрики, который он использует для создания объектов, и объект фабрики может быть заменен во время выполнения.
Шаблоны проектирования описывают абстрактный шаблон фабрики как «интерфейс для создания семейств связанных или зависимых объектов без указания их конкретных классов». [4]
Фабрика определяет конкретный тип создаваемого объекта, и именно здесь объект фактически создается. Однако фабрика возвращает только ссылку (в Java, например, оператором new ) или указатель абстрактного типа на созданный конкретный объект.
Это изолирует клиентский код от создания объектов , поскольку клиенты запрашивают у объекта-фабрики создание объекта требуемого абстрактного типа и возвращают абстрактный указатель на объект. [5]
Примером может служить абстрактный класс фабрики DocumentCreator
, который предоставляет интерфейсы для создания ряда продуктов (например, createLetter()
и createResume()
). Система будет иметь любое количество производных конкретных версий класса, DocumentCreator
таких как FancyDocumentCreator
или ModernDocumentCreator
, каждая с различной реализацией createLetter()
и , createResume()
которые будут создавать соответствующие объекты, такие как FancyLetter
или ModernResume
. Каждый из этих продуктов является производным от простого абстрактного класса, такого как Letter
или Resume
, о котором клиент знает. Клиентский код получит соответствующий экземплярDocumentCreator
и вызовет его методы фабрики . Каждый из полученных объектов будет создан из одной и той же DocumentCreator
реализации и будет иметь общую тему. Клиенту нужно будет знать только, как обращаться с абстрактным Letter
или Resume
классом, а не с конкретной версией, созданной конкретной фабрикой.
Поскольку фабрика возвращает только ссылку или указатель на абстрактный тип, клиентский код, запросивший объект у фабрики, не знает — и не обременен — фактическим конкретным типом созданного объекта. Однако абстрактная фабрика знает тип конкретного объекта (и, следовательно, конкретной фабрики). Например, фабрика может считывать тип объекта из файла конфигурации. Клиенту не нужно указывать тип, так как тип уже указан в файле конфигурации. В частности, это означает:
В приведенной выше диаграмме классов 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++23 основана на реализации, описанной в книге до C++98.
импорт стандартный ; enum 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 :: println ( "Room::setSide {} ms" , d ); } virtual void enter () {} Room ( const Room & ) = delete ; // правило трех Room & оператор = ( const Room & ) = delete ; private : int roomNumber ; }; класс Стена : public MapSite { public : Стена () {} 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 ; // правило трех Door & оператор = ( const Door & ) = delete ; private : Room * room1 ; Room * room2 ; }; class Maze { public : void addRoom ( Room * r ) { std :: println ( "Maze::addRoom {}" , r ); } Room * roomNo ( int ) const { return nullptr ; } }; класс MazeFactory { public : MazeFactory () = default ; virtual ~ MazeFactory () = default ; виртуальный лабиринт * makeMaze () const { return new лабиринт ; } виртуальная стена * makeWall () const { return new стена ; } виртуальная комната * makeRoom ( int n ) const { return new комната ( n ); } виртуальная дверь * makeDoor ( комната * r1 , комната * r2 ) const { return new дверь ( r1 , r2 ); } }; // Если createMaze передается объект в качестве параметра для создания комнат, стен и дверей, то можно изменить классы комнат, стен и дверей, передав другой параметр. Это пример шаблона Abstract Factory (99).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 ( Direction :: North , factory.makeWall ()); r1 - > setSide ( Direction :: East , aDoor ) ; r1 - > setSide ( Direction :: South , factory.makeWall ( ) ); r1 - > setSide ( Direction :: West , factory.makeWall ( )); r2 - > setSide ( Direction :: North , factory.makeWall ( ) ) ; r2 -> setSide ( Направление :: Восток , factory.makeWall ()); r2 - > setSide ( Направление :: Юг , factory.makeWall ( ) ); r2 - > setSide ( Направление :: Запад , 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
Создание объектов: Абстрактная фабрика: Цель: Предоставить интерфейс для создания семейств связанных или зависимых объектов без указания их конкретных классов.
{{cite web}}
: CS1 maint: бот: исходный статус URL неизвестен ( ссылка )Фабрика изолирует клиента от изменений в продукте или способе его создания, и она может обеспечить эту изоляцию для объектов, полученных из очень разных абстрактных интерфейсов.
{{cite web}}
: CS1 maint: бот: исходный статус URL неизвестен ( ссылка )