Abstract Factory Pattern in C++



The Abstract Factory is a creational design pattern that provides an interface with methods for a particular type of object, but it allows subclasses to alter the type of objects that will be created. The Abstract Factory pattern is used when we have multiple families of related or dependent objects and we want to create them without specifying their concrete classes.

In other words, it is a factory of factories. In the last chapter, we discussed the Factory Method pattern which is used to create objects of a single family. But in Abstract Factory pattern, we can create objects of multiple families.

Imagine you are building your dream house, and you have two choices for furniture: Modern or Victorian. Each type has its own matching set of items like a chair, a sofa, and a coffee table. Instead of picking each item one by one and possibly making a mix-match, you use a furniture factory that gives you the full set in one style. If you want Modern, you get all Modern items. If you want Victorian, you get all Victorian items. The Abstract Factory pattern works the same way: it gives you a whole set of related objects in one style, so everything fits together perfectly.

Implementation of Abstract Factory Pattern in C++

Following are the some important ways to implement Abstract Factory Pattern in C++

  • Conventional Abstract Factory − It uses classic interface and concrete factories.
  • Registry-based Abstract Factory − It uses a registry of factories to create objects of different families.

Concrete Factory means a class which implements the Abstract Factory interface and is responsible for creating the objects of a particular family. The client code uses the abstract factory interface to create the objects without knowing the concrete classes.

Conventional Abstract Factory Implementation in C++

In this approach, we create an abstract factory interface which is used for defining the methods for creating family of related objects. Then, we create concrete factory classes that implement the abstract factory interface and provide the implementation for creating the objects of a particular family.

Example of Abstract Factory Pattern in C++

In the below example, we have an abstract factory interface FurnitureFactory that defines the methods for creating family of related objects. We have two concrete factory classes ModernFurnitureFactory and VictorianFurnitureFactory that implement the FurnitureFactory interface and provide the implementation for creating the objects of a particular family. The client code uses the abstract factory interface to create the objects without knowing the concrete classes.

Abstract Factory Pattern in C++

Let's implement a simple example of Abstract Factory Pattern in C++

#include <iostream>
#include <string>
using namespace std;
// Abstract Product A
class Chair {
   public:
      virtual string getType() = 0;
};
// Abstract Product B
class Sofa {
   public:
      virtual string getType() = 0;
};
// Concrete Product A1
class ModernChair : public Chair {
   public:
      string getType() {
         return "Modern Chair";
      }
};
// Concrete Product B1
class ModernSofa : public Sofa {
   public:
      string getType() {
         return "Modern Sofa";
      }
};
// Concrete Product A2
class VictorianChair : public Chair {
   public:
      string getType() {
         return "Victorian Chair";
      }
};
// Concrete Product B2
class VictorianSofa : public Sofa {
   public:
      string getType() {
         return "Victorian Sofa";
      }
};
// Abstract Factory
class FurnitureFactory {
   public:
      virtual Chair* createChair() = 0;
      virtual Sofa* createSofa() = 0;
};
// Concrete Factory 1
class ModernFurnitureFactory : public FurnitureFactory {
   public:
      Chair* createChair() {
         return new ModernChair();
      }
      Sofa* createSofa() {
         return new ModernSofa();
      }
};
// Concrete Factory 2
class VictorianFurnitureFactory : public FurnitureFactory {
   public:
      Chair* createChair() {
         return new VictorianChair();
      }
      Sofa* createSofa() {
         return new VictorianSofa();
      }
};
int main() {
   FurnitureFactory* factory;
   Chair* chair;
   Sofa* sofa;

   // Create Modern Furniture
   factory = new ModernFurnitureFactory();
   chair = factory->createChair();
   sofa = factory->createSofa();
   cout << "Created the Modern furniture:" << endl;
   cout << "Chair: ";
   cout << chair->getType() << endl;
   cout << "Sofa: ";
   cout << sofa->getType() << endl;

   // Create Victorian Furniture
   factory = new VictorianFurnitureFactory();
   chair = factory->createChair();
   sofa = factory->createSofa();
   cout << "Created the Victorian furniture:" << endl;
   cout << "Chair: ";
   cout << chair->getType() << endl;
   cout << "Sofa: ";
   cout << sofa->getType() << endl;

   return 0;
}

Following is the output of the above program −

Created the Modern furniture:
Chair:
Modern Chair
Sofa:
Modern Sofa
Created the Victorian furniture:
Chair:
Victorian Chair
Sofa:
Victorian Sofa

Registry-based Abstract Factory Implementation in C++

This is a similar approach we have discussed in the Factory Method pattern. In this approach, we create a registry of factories that can create objects of different families. The registry is a map that maps the family name to the corresponding factory. The client code uses the registry to get the factory for a particular family and then uses the factory to create the objects.

Example of Registry-based Abstract Factory Pattern in C++

Let's take an example of a car factory. Where, we have an abstract class CarFactory that defines the factory method createCar(). Then, we create concrete subclasses such as SUVFactory and SedanFactory that implement the factory method to create specific types of cars. We also create a registry of factories that maps the family name to the corresponding factory.

Registry based Abstract Factory Pattern in C++

Following is a simple example of Registry-based Abstract Factory Pattern in C++

#include <iostream>
#include <string>
#include <map>

using namespace std;
// Product interface
class Car {
   public:
      virtual string getType() = 0;
};
// Concrete Products
class SUV : public Car {
   public:
      string getType() {
         return "SUV";
      }
};
class Sedan : public Car {
   public:
      string getType() {
         return "Sedan";
      }
};
// Factory interface
class CarFactory {
   public:
      virtual Car* createCar() = 0;
};
// Concrete Factories
class SUVFactory : public CarFactory {
   public:
      Car* createCar() {
         return new SUV();
      }
};
class SedanFactory : public CarFactory {
   public:
      Car* createCar() {
         return new Sedan();
      }
};
// Registry of factories
class CarFactoryRegistry {
   private:
      map<string, CarFactory*> registry;
   public:
      void registerFactory(string type, CarFactory* factory) {
         registry[type] = factory;
      }
      CarFactory* getFactory(string type) {
         return registry[type];
      }
};
int main() {
   CarFactoryRegistry registry;
   registry.registerFactory("SUV", new SUVFactory());
   registry.registerFactory("Sedan", new SedanFactory());

   CarFactory* factory;
   Car* car;

   // Create SUV
   factory = registry.getFactory("SUV");
   car = factory->createCar();
   cout << "Created car of type: ";
   cout << car->getType() << endl;

   // Create Sedan
   factory = registry.getFactory("Sedan");
   car = factory->createCar();
   cout << "Created car of type: ";
   cout << car->getType() << endl;

   return 0;
}

Following is the output of the above program −

Created car of type:SUV
Created car of type:Sedan

Pro's and Cons of Abstract Factory Pattern

Following are the some important pros and cons of Abstract Factory Pattern −

Pros Cons
Makes groups of matching objects easy to create without worrying about details. Can make code bigger and more complex because you need extra factory classes.
Keeps code flexible and lets you swap object families easily. Lots of classes if you have many object families.
Easy to add new object families without changing old code. More work to update and keep track of all the factories.
Ensures everything matches—no mix-and-match mistakes. Too much for simple cases where one factory is enough.

When to use Abstract Factory Pattern?

Following are the some important scenarios where we can use Abstract Factory Pattern −

  • When the system needs to be independent of how its products are created, composed, and represented.
  • When the system is configured with one of multiple families of products.
  • When we want to provide a library of products, and we want to reveal just their interfaces, not their implementations.
  • When we want to ensure that the client code uses only objects from the same family.

Conclusion

Abstract Factory is a creational software design pattern that provides an interface for generating families of related or dependent objects without revealing their concrete classes. It is used when we have multiple families of related or dependent objects and want to create them without having their concrete classes defined. The Abstract Factory pattern conceals the objects' construction and offers loose binding of the concrete classes and the client code, and it also satisfies the Open/Closed Principle, because it is feasible to add new families of related objects without altering the code.

Advertisements