我喜欢使用工厂设计模式来注入依赖项,但这意味着拥有许多非常相似的类(每个类几乎只有一个工厂)。然后我想到了使用模板创建通用工厂模式的想法。像这样:
// 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()
这有效,但有一些问题:
Create()
方法的内容,它可能会失败,因为它会使用Creator()
方法的错误实例。有没有办法来解决这个问题?这就是为什么创建obj3失败的原因。 SetCreator()方法只能使用std :: function对象。我理解为什么。我的问题是,我可以更改它以采取任何适当的方法并调用正确的Creator()
方法吗?
理想情况下,它会有这个声明:
template < typename F >
static void SetCreator( F fn );
然后我可以这样做:
std::shared_ptr< A > foo( int, float ) { return new B; };
FactoryParamsConstructor::SetCreator( foo );
答案 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); },
};
答案 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... );
}
};