实施通用工厂设计模式

时间:2018-05-03 12:45:59

标签: c++ templates design-patterns factory-method

我喜欢使用工厂设计模式来注入依赖项,但这意味着拥有许多非常相似的类(每个类几乎只有一个工厂)。然后我想到了使用模板创建通用工厂模式的想法。像这样:

// g++ -std=c++14 -Wall -Wextra factory.cpp -o factory


#include <functional>
#include <memory>
#include <utility>
#include <iostream>



template < typename T, typename Base = typename T::Iface >
class Factory
{
public:

    template < typename... Args >
    using CreatorFn = std::function< std::shared_ptr< Base > ( Args&&... ) >;

    template < typename... Args >
    static std::shared_ptr< Base > Create( Args&&... args );

    template < typename... Args >
    static void ResetToDefaultCreator();

    template < typename... Args >
    static void SetCreator( CreatorFn< Args... > fn );

private:
    Factory() = delete;

    template < typename... Args >
    static CreatorFn< Args... >& Creator();

    template < typename... Args >
    static std::shared_ptr< Base > DefaultCreator( Args&&... args );
};



template < typename T, typename Base >
template < typename... Args >
std::shared_ptr< Base > Factory< T, Base >::Create( Args&&... args )
{
    return Creator< Args... >()( std::forward< Args >( args )... );
}


template < typename T, typename Base >
template < typename... Args >
void Factory< T, Base >::ResetToDefaultCreator()
{
    CreatorFn< Args... >() = DefaultCreator< Args... >;
}

template < typename T, typename Base >
template < typename... Args >
void Factory< T, Base >::SetCreator( CreatorFn< Args... > fn )
{
    Creator< Args... >() = fn;
}

template < typename T, typename Base >
template < typename... Args >
Factory< T, Base >::CreatorFn< Args... >& Factory< T, Base >::Creator()
{
    static CreatorFn< Args... > creator = DefaultCreator< Args... >;
    return creator;
}

template < typename T, typename Base >
template < typename... Args >
std::shared_ptr< Base > Factory< T, Base >::DefaultCreator( Args&&... args )
{
    return std::make_shared< T >( std::forward< Args >( args )... );
}


struct A {
    virtual ~A() = default;

    virtual void foo() = 0;
};

struct B : public A {
    using Iface = A;

    virtual void foo()
    {
        std::cout << "-- B::foo()" << std::endl;
    }
};

struct C : public A {
    using Iface = A;

    C( int, float )
    {
    }

    virtual void foo()
    {
        std::cout << "-- C::foo()" << std::endl;
    }
};

struct D : public A {
    using Iface = A;

    D( int, float )
    {
    }

    virtual void foo()
    {
        std::cout << "-- D::foo()" << std::endl;
    }
};

using FactoryDefaultConstructor = Factory< B >;
using FactoryParamsConstructor  = Factory< C >;

struct MyClass
{
    MyClass() : a( FactoryParamsConstructor::Create( 3, 5.7f ) )
    {}

    void foo()
    {
        a->foo();
    }
private:
    std::shared_ptr< A > a;
};



int main()
{
    FactoryParamsConstructor::ResetToDefaultCreator<int,float>();

    std::shared_ptr< A > obj1 = FactoryParamsConstructor::Create( 3, 5 );
    C* realObj1 = dynamic_cast< C* >( obj1.get() );
    if ( nullptr != realObj1 )
    {
        std::cout << "1 created" << std::endl;
    }
    else
    {
        std::cout << "1 failed" << std::endl;
    }

    MyClass class1;
    class1.foo();

    FactoryParamsConstructor::CreatorFn< int, float > newCretorFn = []( int a,float b ){
                                    std::cout << "****cb called"<<std::endl;
                                    return std::shared_ptr< A >( new D( a, b ) );
                            };
    FactoryParamsConstructor::SetCreator< int, float >( newCretorFn );
    std::shared_ptr< A > obj2 = FactoryParamsConstructor::Create( 3, 5.7f );
    D* realObj2 = dynamic_cast< D* >( obj2.get() );
    if ( nullptr != realObj2 )
    {
        std::cout << "2 created" << std::endl;
    }
    else
    {
        std::cout << "2 failed" << std::endl;
    }

    float p = 5.5f;
    std::shared_ptr< A > obj3 = FactoryParamsConstructor::Create( 3, p );
    D* realObj3 = dynamic_cast< D* >( obj3.get() );
    if ( nullptr != realObj3 )
    {
        std::cout << "3 created" << std::endl;
    }
    else
    {
        std::cout << "3 failed" << std::endl;
    }
    MyClass class2;
    class2.foo();
}

输出:

1 created
-- C::foo()
****cb called
2 created
3 failed
****cb called
-- D::foo()

这有效,但有一些问题:

  1. 如果我不注意我传递给Create()方法的内容,它可能会失败,因为它会使用Creator()方法的错误实例。有没有办法来解决这个问题?这就是为什么创建obj3失败的原因。
  2. SetCreator()方法只能使用std :: function对象。我理解为什么。我的问题是,我可以更改它以采取任何适当的方法并调用正确的Creator()方法吗? 理想情况下,它会有这个声明:

    template < typename F > static void SetCreator( F fn );

  3. 然后我可以这样做:

    std::shared_ptr< A > foo( int, float ) { return new B; };
    FactoryParamsConstructor::SetCreator( foo );
    

2 个答案:

答案 0 :(得分:0)

也许overloaded就是你想要的(C ++ 17,但可能是为C ++ 11实现的):

template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;


const auto Afactory = overloaded {
        []() { return std::make_shared<B>(); },
        [](int a, int b) { return std::make_shared<C>(a, b); },
        [](int a, float b) { return std::make_shared<D>(a, b); },
    };

Demo

答案 1 :(得分:0)

我想出了怎么做,并发布了我的解决方案。

template < typename T, typename Base = typename T::Iface >
class Factory
{
public:

    using ReturnType = std::shared_ptr< Base >;
    template < typename... Args >
    using CreatorFn = std::function< ReturnType ( Args... ) >;


    template < typename... Args >
    static ReturnType Create( Args... args )
    {
        return Creator< decltype( args )... >()( args... );
    }

    template < typename... Args >
    static void ResetToDefaultCreator()
    {
        Creator< Args... >() = &DefaultCreator< Args... >;
    }

    template < typename... Args >
    static void SetCreator( CreatorFn< Args... > fn )
    {
        Creator< Args... >() = fn;
    }


private:

    Factory() = delete;

    template < typename... Args >
    static CreatorFn< Args... >& Creator()
    {
        static_assert( ( std::is_same< Args, std::decay_t< Args > >::value && ... ), "None of creator arguments can have a reference.");

        static CreatorFn< Args... > creator = &DefaultCreator< Args... >;
        return creator;
    }

    template < typename... Args >
    static ReturnType DefaultCreator( Args... args )
    {
        return std::make_shared< T >( args... );
    }
};