Search code examples
c++design-patternsabstract-factory

Polymorphic factory


Consider the following code:

template <typename T>
class DrawerFactory
{
protected:
    DrawerFactory() {};
private:
    virtual shared_ptr<IDrawer> GetDrawer(T settings) = 0;
};

class ConcreteDrawerFactoryA : public DrawerFactory<SettingsA>
{
public:
    shared_ptr<IDrawer> GetDrawer(SettingsA settingsA) override
    {
        if (settingsA.style == A) return make_shared<ConcreteDrawerA>(settingsA.length, settingsA.stroke, settingsA.opacity);
        else return make_shared<ConcreteDrawerB>(20, .5);
    };
};

class ConcreteDrawerFactoryB : public DrawerFactory<SettingsB>
{
public:
    shared_ptr<IDrawer> GetDrawer(SettingsB settingsB) override
    {
        if (settingsB.type == TYPEC) return make_shared<ConcreteDrawerC>(settingsB.width, settingsB.height);
        else return make_shared<ConcreteDrawerD>(10, 2);
    };
};

I can get a drawer by:

ConcreteDrawerFactoryA().GetDrawer(settingsa);

or

ConcreteDrawerFactoryB().GetDrawer(settingsb);

What I'd like to do is:

DrawerFactory().GetDrawer(settingsa);
DrawerFactory().GetDrawer(settingsb);

Is there a way to set this up without having to continually add overloads to DrawerFactory for each concrete factory I want to add?


Solution

  • Instead of factory hierarchy and virtual dispatch you could make use of templates and specialization:

    #include <memory>
    
    struct IDrawer { };
    struct Drawer1: IDrawer { };
    struct Drawer2: IDrawer { };
    struct Drawer3: IDrawer { };
    struct Drawer4: IDrawer { };
    
    template <class T>
    struct DrawerGetterImpl;
    
    struct DrawerFactory {
        template <class T>
        std::shared_ptr<IDrawer> GetDrawer(T settings) {
            return DrawerGetterImpl<T>::GetDrawer(settings);
        }
    };
    
    struct SettingsA { int style; };
    
    template <>
    struct DrawerGetterImpl<SettingsA> {
        static std::shared_ptr<IDrawer> GetDrawer(SettingsA settings) {
            if (settings.style == 1) {
                return std::make_shared<Drawer1>();
            }
            return std::make_shared<Drawer2>();
        }
    };
    
    struct SettingsB { int type; };
    
    template <>
    struct DrawerGetterImpl<SettingsB> {
        static std::shared_ptr<IDrawer> GetDrawer(SettingsB settings) {
            if (settings.type == 1) {
                return std::make_shared<Drawer3>();
            }
            return std::make_shared<Drawer4>();
        }
    };
    
    int main() {
        DrawerFactory().GetDrawer(SettingsA{1});
    }
    

    [live demo]