Abstract Factory Pattern


The Abstract Factory design pattern is like a factory of factories. It provides an interface for creating families of related objects, but it doesn’t specify the exact classes of those objects. Instead, it lets the concrete factories decide which classes to create. Each concrete factory is responsible for creating objects that belong to the same family and work together cohesively.

Imagine you are designing a game that can run on different platforms (e.g., PC, Xbox, PlayStation). Each platform requires different sets of objects, such as characters, weapons, and backgrounds. The Abstract Factory pattern helps you create these objects in a platform-specific manner without cluttering your code with platform-specific logic.

Abstract Factory C++ Example:

Let’s see example to illustrate the Abstract Factory pattern for creating shapes and colors.

We’ll have two families of objects: shapes (e.g., Circle, Square) and colors (e.g., Red, Blue).

We’ll create an abstract factory to create shapes and colors, and then two concrete factories to generate specific shapes and colors.

#include <iostream>

// Abstract Shape class
class Shape {
public:
    virtual void draw() = 0;
};

// Concrete Shape classes
class Circle : public Shape {
public:
    void draw() override {
        std::cout << "Drawing a Circle." << std::endl;
    }
};

class Square : public Shape {
public:
    void draw() override {
        std::cout << "Drawing a Square." << std::endl;
    }
};

// Abstract Color class
class Color {
public:
    virtual void fill() = 0;
};

// Concrete Color classes
class Red : public Color {
public:
    void fill() override {
        std::cout << "Filling with Red color." << std::endl;
    }
};

class Blue : public Color {
public:
    void fill() override {
        std::cout << "Filling with Blue color." << std::endl;
    }
};

// Abstract Factory class
class AbstractFactory {
public:
    virtual Shape* createShape() = 0;
    virtual Color* createColor() = 0;
};

// Concrete Factory classes for Shape family
class ShapeFactory : public AbstractFactory {
public:
    Shape* createShape() override {
        // Here, we create a Circle, but it could be any shape based on some logic.
        return new Circle();
    }

    Color* createColor() override {
        // Shape factory does not create colors, return nullptr
        return nullptr;
    }
};

// Concrete Factory classes for Color family
class ColorFactory : public AbstractFactory {
public:
    Shape* createShape() override {
        // Color factory does not create shapes, return nullptr
        return nullptr;
    }

    Color* createColor() override {
        // Here, we create a Red color, but it could be any color based on some logic.
        return new Red();
    }
};

// Client class that uses the Abstract Factory
class Client {
public:
    void drawAndFill(AbstractFactory* factory) {
        Shape* shape = factory->createShape();
        Color* color = factory->createColor();

        if (shape) {
            shape->draw();
            delete shape;
        }

        if (color) {
            color->fill();
            delete color;
        }
    }
};

int main() {
    Client client;

    // Using Shape factory to create a Circle and no color.
    AbstractFactory* shapeFactory = new ShapeFactory();
    client.drawAndFill(shapeFactory);
    delete shapeFactory;

    // Using Color factory to create no shape and a Red color.
    AbstractFactory* colorFactory = new ColorFactory();
    client.drawAndFill(colorFactory);
    delete colorFactory;

    return 0;
}

Explanation of the Code:
In this example, we have two families of objects: shapes (Circle, Square) and colors (Red, Blue). We define an abstract AbstractFactory class with two pure virtual methods: createShape() and createColor(). These methods are responsible for creating shapes and colors, respectively.

We create two concrete factories: ShapeFactory and ColorFactory, each of which implements the AbstractFactory interface. The ShapeFactory creates shapes (here, a Circle) and returns nullptr for colors, while the ColorFactory creates colors (here, Red) and returns nullptr for shapes.

The Client class uses the Abstract Factory pattern by accepting an AbstractFactory object as an argument to its drawAndFill() method. The drawAndFill() method creates a shape and a color using the provided factory, if available, and calls their respective methods (draw() and fill()).

In the main() function, we demonstrate how the Abstract Factory pattern works. We create instances of ShapeFactory and ColorFactory, and the Client class uses them to create and draw shapes and fill colors.

The Abstract Factory pattern allows you to create families of related objects without specifying their concrete classes. It ensures that the created objects are compatible and consistent within a family. By using the Abstract Factory pattern, you can decouple client code from specific object creation, making it easier to add new families of objects without modifying existing code.

Leave a Reply