Written by
Sangwan Kwon
on
on
Extensible abstract factory pattern, c++
This post introduces extensible abstract factory pattern. It is assumed that you know the basic factory pattern and CRTP pattern. The sample code is written by c++14.
The main disadvantage of
abstract factory pattern is extensibility.
Whenverproduct
is added,
both factory
and product
have to be added respectively.
Basic abstract factory pattern
Factory is only responsible for creating products. But we need to add when product is added. This mean the factory is lack of extensibility.
And there are no reusable code. If we need to apply abstract pattern to other product type, we should write code from a to z.
// Product
struct Button {
virtual ~Button() = default;
virtual void draw(void) = 0;
};
struct LinuxButton final : Button {
void draw(void) override { std::cout << "Linux button" << std::endl; }
};
struct MacOSButton final : Button {
void draw(void) override { std::cout << "MacOS button" << std::endl; }
};
// Factory
struct ButtonFactory {
virtual ~ButtonFactory() = default;
virtual std::unique_ptr<Button> create(void) = 0;
};
struct LinuxFactory final : ButtonFactory {
virtual ~LinuxFactory() = default;
std::unique_ptr<Button> create(void) override
{
return std::make_unique<LinuxButton>();
}
};
struct MacOSFactory final : ButtonFactory {
virtual ~MacOSFactory() = default;
std::unique_ptr<Button> create(void) override
{
return std::make_unique<MacOSButton>();
}
};
int main() {
std::unique_ptr<ButtonFactory> factory;
std::unique_ptr<Button> button;
factory = std::make_unique<LinuxFactory>();
button = factory->create();
button->draw();
factory = std::make_unique<MacOSFactory>();
button = factory->create();
button->draw();
return 0;
}
Extensible abstract factory pattern
The purpose of this pattern takes both reusability and extensibility.
For this,
- Couple factory and product with nested struct.
- Make factory reusable with CRTP pattern.
Code is simple.
Reusable factory
// abstract-factory.hpp
template <typename P>
struct AbstractFactory {
virtual std::unique_ptr<P> create(void) = 0;
};
template <typename F, typename P>
struct Factory : AbstractFactory<P> { // Apply CRTP
virtual ~Factory() = default;
struct Product : P { // Couple factory and product
virtual ~Product() = default;
};
std::unique_ptr<P> create(void) override {
return std::make_unique<typename F::Product>(); // Generate concrete product
};
};
Practice
For using extensible abstract pattern, just two step needed.
- Define product struct
- Implement Concrete Product in Concrete Factory
#include "abstract-factory.hpp"
// Product
struct Button {
virtual ~Button() = default;
virtual void draw(void) {};
};
// Factory #1
struct LinuxFactory : Factory<LinuxFactory, Button> {
struct Product : Button {
virtual ~Product() = default;
void draw(void) override { std::cout << "Linux button" << std::endl; }
};
};
// Factory #2
struct MacOSFactory final : Factory<MacOSFactory, Button> {
struct Product : Button {
virtual ~Product() = default;
void draw(void) override { std::cout << "MacOS button" << std::endl; }
};
};
int main() {
std::unique_ptr<AbstractFactory<Button>> factory;
std::unique_ptr<Button> button;
factory = std::make_unique<LinuxFactory>();
button = factory->create();
button->draw();
factory = std::make_unique<MacOSFactory>();
button = factory->create();
button->draw();
return 0;
}