Реализация общего шаблона проектирования фабрики

Мне нравится использовать шаблон проектирования фабрики для внедрения зависимостей, но это означает наличие множества очень похожих классов (почти одна фабрика на класс). Тогда у меня возникла идея создать общий шаблон фабрики с использованием шаблонов. Что-то вроде этого:

// 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 );

Тогда я мог бы сделать это:

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

1

Решение

Может быть 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); },
};

демонстрация

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... );
}
};
0

По вопросам рекламы [email protected]