Introduction to the Abstract Factory Pattern

Introduction to the Abstract Factory Pattern

Welcome back! You’ve already explored the power of the Factory Method Pattern and how it promotes flexibility in your code design. Today, we are moving a step further by diving into the Abstract Factory Pattern. This pattern will help you create families of related objects without specifying their concrete classes.

What You'll Learn

In this lesson, you'll focus on understanding and implementing the Abstract Factory Pattern in C++. Here's what we’ll cover:

  1. Understanding the Abstract Factory Pattern: Learn the core concepts and recognize scenarios where this pattern is beneficial.
  2. Implementing the Abstract Factory: See how to create abstract factories and concrete factories to produce related object families.
  3. Client Code Interaction: Observe how client code can interact with abstract factories without knowing the concrete classes, enhancing flexibility and scalability.

Let's take a look at a part of an example to get a better understanding:

Button.hpp file to define the abstract Button class and its concrete implementations:

#include <iostream>

// Abstract Product 1: Button as an interface
class Button {
public:
    virtual void paint() = 0;
    virtual ~Button() = default;
};

// Concrete Product WinButton that implements Button with custom 'paint' logic
class WinButton : public Button {
public:
    void paint() override {
        std::cout << "Rendering a button in a Windows style." << std::endl;
    }
};

// Concrete Product MacButton that implements Button with custom 'paint' logic
class MacButton : public Button {
public:
    void paint() override {
        std::cout << "Rendering a button in a Mac style." << std::endl;
    }
};

Checkbox.hpp file to define the abstract Checkbox class and its concrete implementations:

// Abstract Product 2: Checkbox as an interface
class Checkbox {
public:
    virtual void paint() = 0;
    virtual ~Checkbox() = default;
};

// Concrete Product WinCheckbox that implements Checkbox with custom 'paint' logic
class WinCheckbox : public Checkbox {
public:
    void paint() override {
        std::cout << "Rendering a checkbox in a Windows style." << std::endl;
    }
};

// Concrete Product MacCheckbox that implements Checkbox with custom 'paint' logic
class MacCheckbox : public Checkbox {
public:
    void paint() override {
        std::cout << "Rendering a checkbox in a Mac style." << std::endl;
    }
};

Factory.hpp file to create an abstract factory and concrete factories for different operating systems and their UI components:

// Abstract Factory class interface for creating buttons and checkboxes
class GUIFactory {
public:
    virtual Button* createButton() = 0;
    virtual Checkbox* createCheckbox() = 0;
    virtual ~GUIFactory() = default;
};

// Concrete Factory WinFactory that creates Windows style buttons and checkboxes by implementing GUIFactory
class WinFactory : public GUIFactory {
public:
    Button* createButton() override {
        return new WinButton();
    }

    Checkbox* createCheckbox() override {
        return new WinCheckbox();
    }
};

// Concrete Factory MacFactory that creates Mac style buttons and checkboxes by implementing GUIFactory
class MacFactory : public GUIFactory {
public:
    Button* createButton() override {
        return new MacButton();
    }

    Checkbox* createCheckbox() override {
        return new MacCheckbox();
    }
};

Application.hpp file to create an application class that interacts with the abstract factory:

// Client code that interacts with the abstract factory to create related objects
class Application {
private:
    GUIFactory* factory;
    Button* button;
    Checkbox* checkbox;

public:
    Application(GUIFactory* f) : factory(f) {
        button = factory->createButton();
        checkbox = factory->createCheckbox();
    }

    void paint() {
        button->paint();
        checkbox->paint();
    }

    ~Application() {
        delete button;
        delete checkbox;
    }
};

main.cpp file to test the code:

int main() {
    GUIFactory* factory = nullptr;
    std::string osType = "Windows";

    // Create a factory based on the OS type
    if (osType == "Windows") {
        factory = new WinFactory();
    } else if (osType == "Mac") {
        factory = new MacFactory();
    }

    if (factory) {
        // Create an application using the factory and paint the UI components
        Application* app = new Application(factory);
        app->paint();

        delete app;
        delete factory;
    } else {
        std::cout << "Unknown OS type." << std::endl;
    }

    return 0;
}

This code snippet gives an insight into how our abstract factory can create related objects, such as buttons and checkboxes for different operating systems.

In the example above, we have two concrete factories, WinFactory and MacFactory, that create Windows and Mac style buttons and checkboxes. The Application class interacts with the abstract factory to create the required objects without knowing the concrete classes. This decouples the client code from the object creation process, making it easier to switch between different object families.

In short for the Abstract Factory Pattern, we need to define abstract product classes and their concrete implementations (e.g., Button, Checkbox, WinButton, MacButton, WinCheckbox, MacCheckbox). Then, we create an abstract factory class and concrete factories for different object families (e.g., GUIFactory, WinFactory, MacFactory). Finally, the client code interacts with the abstract factory to create related objects without knowing their concrete classes.

Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal