Abstract Factory

Design patterns / Creational patterns
image

#include <iostream>
#include <memory>
using namespace std;

//abstract product A
class IProductA {
    public:
    virtual ~IProductA() = default;
    virtual void testA() = 0;
};

//abstract product B
class IProductB {
    public:
    virtual ~IProductB() = default;
    virtual void testB() = 0;
};

//concrete product A1
class ProductA1: public IProductA {
    public:
    void testA() {
        cout << "test A1\n";
    }
};

//concrete product A2
class ProductA2: public IProductA {
    public:
    void testA() {
        cout << "test A2\n";
    }
};

//concrete product B1
class ProductB1: public IProductB {
    public:
    void testB() {
        cout << "test B1\n";
    }
};

//concrete product B2
class ProductB2: public IProductB {
    public:
    void testB() {
        cout << "test B2\n";
    }
};

//abstract factory
class IFactory {
    public:
    virtual ~IFactory() = default;
    virtual unique_ptr<IProductA> createA() = 0;
    virtual unique_ptr<IProductB> createB() = 0;
};

//concrete factory 1
class Factory1: public IFactory {
    public:
    unique_ptr<IProductA> createA() {
        return make_unique<ProductA1>();
    }
    unique_ptr<IProductB> createB() {
        return make_unique<ProductB1>();
    }
};

//concrete factory 2
class Factory2: public IFactory {
    public:
    unique_ptr<IProductA> createA() {
        return make_unique<ProductA2>();
    }
    unique_ptr<IProductB> createB() {
        return make_unique<ProductB2>();
    }
};

//client code
void testFactory(IFactory& factory) {
    unique_ptr<IProductA> productA = factory.createA();
    unique_ptr<IProductB> productB = factory.createB();
    productA->testA();
    productB->testB();
}

Factory1 factory1;
testFactory(factory1);
//printed: test A1
//         test B1
Factory2 factory2;
testFactory(factory2);
//printed: test A2
//         test B2


Provides an interface for creating families of objects whose interfaces are known but concrete classes are not.
Read more

The problem. A program draws its windows in two styles, light and dark. Each style needs its own button and its own menu. If the code creates them directly, it can mix a light button with a dark menu by mistake. A third style would also mean a search for every place where a button or a menu is created.

The idea. An abstract factory is an interface with one creation method for each kind of product. Each concrete factory implements it for one family. One factory creates the light button and the light menu; another creates the dark ones. The client receives a factory and creates every product through it. It never names a concrete product, so the products it gets always belong to one family. To change the whole family, you give the client a different factory.

How to read the example. There are two kinds of product, A and B. A concrete factory makes one family: one product of each kind, marked with the same number. The client receives a factory, asks it for one product of each kind and uses both. The client does not know which family it got, but the output shows it. Some languages declare no interfaces at all, but the roles stay the same.

When to use it. Use Abstract Factory when products must work together and there are several families of them, such as visual themes or operating systems. Do not use it when there is only one family. It is also not needed when the products do not depend on each other: a constructor or a Factory Method is enough.

A common mistake. Choosing this pattern while the kinds of product still change often. Each new kind adds a method to the abstract factory and to every concrete factory.