C++ Abstract Factory Pattern Example

1. Definition

The Abstract Factory Design Pattern is a creational pattern that provides an interface for creating families of related or dependent objects without specifying their concrete classes. It involves multiple Factory Methods, one for each type of object to be created.

2. Problem Statement

Imagine you are designing a UI toolkit that should be consistent across multiple platforms like Windows and MacOS. However, each platform has different appearances for UI elements like buttons, checkboxes, and windows. How can we ensure that the toolkit remains consistent in appearance within each platform while also being adaptable across multiple platforms?

3. Solution

The Abstract Factory Pattern solves this problem by defining an abstract factory interface that declares the creation methods for a family of products. Then, for each platform, a concrete factory is created that implements these methods and produces products conforming to the platform's look and feel.

4. Real-World Use Cases

1. A cross-platform UI library where each operating system should have consistent UI elements but different from other operating systems.

2. Providing support for different database families in an application where each database family (e.g., SQL, NoSQL) has different types of databases with unique configurations.

5. Implementation Steps

1. Define abstract product interfaces for each type of product.

2. Implement concrete product classes for each platform.

3. Define an abstract factory interface that declares a set of methods for creating each abstract product.

4. Implement a concrete factory for each platform that produces the platform-specific products.

5. Use the concrete factory to create platform-consistent products.

6. Implementation in C++

// Abstract products
class Button {
public:
    virtual void render() = 0;
};
class Checkbox {
public:
    virtual void check() = 0;
};
// Concrete products for Windows
class WindowsButton : public Button {
public:
    void render() {
        std::cout << "Rendering a Windows button." << std::endl;
    }
};
class WindowsCheckbox : public Checkbox {
public:
    void check() {
        std::cout << "Checking a Windows checkbox." << std::endl;
    }
};
// Concrete products for MacOS
class MacOSButton : public Button {
public:
    void render() {
        std::cout << "Rendering a MacOS button." << std::endl;
    }
};
class MacOSCheckbox : public Checkbox {
public:
    void check() {
        std::cout << "Checking a MacOS checkbox." << std::endl;
    }
};
// Abstract factory
class GUIFactory {
public:
    virtual Button* createButton() = 0;
    virtual Checkbox* createCheckbox() = 0;
};
// Concrete factory for Windows
class WindowsFactory : public GUIFactory {
public:
    Button* createButton() {
        return new WindowsButton();
    }
    Checkbox* createCheckbox() {
        return new WindowsCheckbox();
    }
};
// Concrete factory for MacOS
class MacOSFactory : public GUIFactory {
public:
    Button* createButton() {
        return new MacOSButton();
    }
    Checkbox* createCheckbox() {
        return new MacOSCheckbox();
    }
};
int main() {
    GUIFactory* factory = new WindowsFactory();
    Button* btn = factory->createButton();
    Checkbox* chkbox = factory->createCheckbox();
    btn->render();
    chkbox->check();
    delete btn;
    delete chkbox;
    delete factory;
    return 0;
}

Output:

Rendering a Windows button.
Checking a Windows checkbox.

Explanation:

1. Button and Checkbox are abstract product interfaces defining the expected behaviors.

2. Concrete products WindowsButton, WindowsCheckbox, MacOSButton, and MacOSCheckbox implement these behaviors according to platform specifics.

3. GUIFactory is the abstract factory interface declaring methods to create each product.

4. WindowsFactory and MacOSFactory are the concrete factories that instantiate the platform-specific products.

5. In the main function, a Windows factory is used as an example to create a button and checkbox consistent with the Windows platform look.

7. When to use?

The Abstract Factory pattern is best used when:

1. The system needs to be independent of how its objects are created, composed, and represented.

2. The system is configured with multiple families of products.

3. Products from a family are designed to work together, and this constraint must be enforced.

4. You want to provide a library of products and reveal only their interfaces, not their implementations.


Comments