FazBrowse Wikipedia Viewer | Main Page |
Search Wikipedia:
|
Viewing: https://en.wikipedia.org/wiki/Abstract_Factory_Pattern

Abstract factory pattern

UML class diagram

The abstract factory pattern in software engineering is a design pattern that provides a way to create families of related objects without imposing their concrete classes, by encapsulating a group of individual factories that have a common theme without specifying their concrete classes. According to this pattern, a client software component creates a concrete implementation of the abstract factory and then uses the generic interface of the factory to create the concrete objects that are part of the family. The client does not know which concrete objects it receives from each of these internal factories, as it uses only the generic interfaces of their products. This pattern separates the details of implementation of a set of objects from their general usage and relies on object composition, as object creation is implemented in methods exposed in the factory interface.

Use of this pattern enables interchangeable concrete implementations without changing the code that uses them, even at runtime. However, employment of this pattern, as with similar design patterns, may result in unnecessary complexity and extra work in the initial writing of code. Additionally, higher levels of separation and abstraction can result in systems that are more difficult to debug and maintain.

In the above UML class diagram, the Client class that requires ProductA and ProductB objects does not instantiate the ProductA1 and ProductB1 classes directly. Instead, the Client refers to the AbstractFactory interface for creating objects, which makes the Client independent of how the objects are created (which concrete classes are instantiated). The Factory1 class implements the AbstractFactory interface by instantiating the ProductA1 and ProductB1 classes.

The UML sequence diagram shows the runtime interactions. The Client object calls createProductA() on the Factory1 object, which creates and returns a ProductA1 object. Thereafter, the Client calls createProductB() on Factory1, which creates and returns a ProductB1 object.

Variants

edit

The original structure of the abstract factory pattern, as defined in 1994 in Design Patterns, is based on abstract classes for the abstract factory and the abstract products to be created. The concrete factories and products are classes that specialize the abstract classes using inheritance.

A more recent structure of the pattern is based on interfaces that define the abstract factory and the abstract products to be created. This design uses native support for interfaces or protocols in mainstream programming languages to avoid inheritance. In this case, the concrete factories and products are classes that realize the interface by implementing it.

Example

edit

This C++23 implementation is based on the pre-C++98 implementation in the book.

> sides;\npublic:\n explicit Room(int n = 0): \n roomNumber{n} {}\n\n ~Room() = default;\n\n Room& setSide(MapSite::Direction d, MapSite& ms) {\n sides[std::to_underlying(d)] = std::move(ms);\n std::println(\"Room::setSide {} ms\", d);\n return *this;\n }\n\n virtual void enter() override = 0;\n\n Room(const Room&) = delete;\n Room& operator=(const Room&) = delete;\n};\n\nclass Wall: public MapSite {\npublic:\n explicit Wall(int n = 0):\n MapSite(n) {}\n\n ~Wall() = default;\n\n void enter() override {\n // ...\n }\n};\n\nclass Door: public MapSite {\nprivate:\n shared_ptr<Room> room1;\n shared_ptr<Room> room2;\npublic:\n explicit Door(int n = 0, shared_ptr<Room> r1 = nullptr, shared_ptr<Room> r2 = nullptr): \n MapSite(n), room1{std::move(r1)}, room2{std::move(r2)} {}\n\n ~Door() = default;\n\n void enter() override {\n // ...\n }\n\n Door(const Door&) = delete;\n Door& operator=(const Door&) = delete;\n};\n\nclass Maze {\nprivate:\n vector<shared_ptr<Room>> rooms;\npublic:\n Maze() = default;\n ~Maze() = default;\n\n Maze& addRoom(shared_ptr<Room> r) {\n std::println(\"Maze::addRoom {}\", reinterpret_cast<void*>(r.get()));\n rooms.push_back(std::move(r));\n return *this;\n }\n \n shared_ptr<Room> roomNo(int n) const {\n for (const Room& r: rooms) {\n // actual lookup logic here...\n }\n return nullptr;\n }\n};\n\nclass MazeFactory {\npublic:\n MazeFactory() = default;\n\n virtual ~MazeFactory() = default;\n\n [[nodiscard]]\n unique_ptr<Maze> makeMaze() const {\n return std::make_unique<Maze>();\n }\n\n [[nodiscard]]\n shared_ptr<Wall> makeWall() const {\n return std::make_shared<Wall>();\n }\n\n [[nodiscard]]\n shared_ptr<Room> makeRoom(int n) const {\n return std::make_shared<Room>(new Room(n));\n }\n \n [[nodiscard]]\n shared_ptr<Door> makeDoor(shared_ptr<Room> r1, shared_ptr<Room> r2) const {\n return std::make_shared<Door>(std::move(r1), std::move(r2));\n }\n};\n\n// 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.\n\nclass MazeGame {\npublic:\n Maze() = default;\n ~Maze() = default;\n\n [[nodiscard]]\n unique_ptr<Maze> createMaze(MazeFactory& factory) {\n unique_ptr<Maze> maze = factory.makeMaze();\n shared_ptr<Room> r1 = factory.makeRoom(1);\n shared_ptr<Room> r2 = factory.makeRoom(2);\n shared_ptr<Door> door = factory.makeDoor(r1, r2);\n maze->addRoom(r1)\n .addRoom(r2)\n .setSide(MapSite::Direction::NORTH, factory.makeWall())\n .setSide(MapSite::Direction::EAST, door)\n .setSide(MapSite::Direction::SOUTH, factory.makeWall())\n .setSide(MapSite::Direction::WEST, factory.makeWall())\n .setSide(MapSite::Direction::NORTH, factory.makeWall())\n .setSide(MapSite::Direction::EAST, factory.makeWall())\n .setSide(MapSite::Direction::SOUTH, factory.makeWall())\n .setSide(MapSite::Direction::WEST, door);\n return maze;\n }\n};\n\nint main(int argc, char* argv[]) {\n MazeGame game;\n unique_ptr<Maze> maze = game.createMaze(MazeFactory());\n}\n"}}'>importstd; usingstd::array; usingstd::shared_ptr; usingstd::unique_ptr; usingstd::vector; classMapSite{ public: enumclassDirection:char{ NORTH, SOUTH, EAST, WEST, }; virtualvoidenter()=0; virtual~MapSite()=default; }; classRoom:publicMapSite{ private: introomNumber; shared_ptr<array<MapSite,4>>sides; public: explicitRoom(intn=0): roomNumber{n}{} ~Room()=default; Room&setSide(MapSite::Directiond,MapSite&ms){ sides[std::to_underlying(d)]=std::move(ms); std::println("Room::setSide {} ms",d); return*this; } virtualvoidenter()override=0; Room(constRoom&)=delete; Room&operator=(constRoom&)=delete; }; classWall:publicMapSite{ public: explicitWall(intn=0): MapSite(n){} ~Wall()=default; voidenter()override{ // ... } }; classDoor:publicMapSite{ private: shared_ptr<Room>room1; shared_ptr<Room>room2; public: explicitDoor(intn=0,shared_ptr<Room>r1=nullptr,shared_ptr<Room>r2=nullptr): MapSite(n),room1{std::move(r1)},room2{std::move(r2)}{} ~Door()=default; voidenter()override{ // ... } Door(constDoor&)=delete; Door&operator=(constDoor&)=delete; }; classMaze{ private: vector<shared_ptr<Room>>rooms; public: Maze()=default; ~Maze()=default; Maze&addRoom(shared_ptr<Room>r){ std::println("Maze::addRoom {}",reinterpret_cast<void*>(r.get())); rooms.push_back(std::move(r)); return*this; } shared_ptr<Room>roomNo(intn)const{ for(constRoom&r:rooms){ // actual lookup logic here... } returnnullptr; } }; classMazeFactory{ public: MazeFactory()=default; virtual~MazeFactory()=default; [[nodiscard]] unique_ptr<Maze>makeMaze()const{ returnstd::make_unique<Maze>(); } [[nodiscard]] shared_ptr<Wall>makeWall()const{ returnstd::make_shared<Wall>(); } [[nodiscard]] shared_ptr<Room>makeRoom(intn)const{ returnstd::make_shared<Room>(newRoom(n)); } [[nodiscard]] shared_ptr<Door>makeDoor(shared_ptr<Room>r1,shared_ptr<Room>r2)const{ returnstd::make_shared<Door>(std::move(r1),std::move(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. classMazeGame{ public: Maze()=default; ~Maze()=default; [[nodiscard]] unique_ptr<Maze>createMaze(MazeFactory&factory){ unique_ptr<Maze>maze=factory.makeMaze(); shared_ptr<Room>r1=factory.makeRoom(1); shared_ptr<Room>r2=factory.makeRoom(2); shared_ptr<Door>door=factory.makeDoor(r1,r2); maze->addRoom(r1) .addRoom(r2) .setSide(MapSite::Direction::NORTH,factory.makeWall()) .setSide(MapSite::Direction::EAST,door) .setSide(MapSite::Direction::SOUTH,factory.makeWall()) .setSide(MapSite::Direction::WEST,factory.makeWall()) .setSide(MapSite::Direction::NORTH,factory.makeWall()) .setSide(MapSite::Direction::EAST,factory.makeWall()) .setSide(MapSite::Direction::SOUTH,factory.makeWall()) .setSide(MapSite::Direction::WEST,door); returnmaze; } }; intmain(intargc,char*argv[]){ MazeGamegame; unique_ptr<Maze>maze=game.createMaze(MazeFactory()); }

The program output is:

Gamma, Erich; Richard Helm; Ralph Johnson; John M. Vlissides (2009-10-23). "Design Patterns: Abstract Factory". informIT. Archived from the original on 2012-05-16. Retrieved 2012-05-16. Object Creational: Abstract Factory: Intent: Provide an interface for creating families of related or dependent objects without specifying their concrete classes.{{cite web}}: CS1 maint: bot: original URL status unknown (link)
  • ↑ "The Abstract Factory design pattern - Structure and Collaboration". w3sDesign.com. Retrieved 2017-08-12.
  • ↑

  • View original on Wikipedia | Back | FazBrowse Home