Я реализовал общую фабрику объектов, следуя указаниям книги Андрея Александреску Современный дизайн C ++ , поэтому я могу определить иерархию классов следующим образом (я напишу код наиболее упрощенным способом, избегая деталей реализации) или вопросы выделения / освобождения памяти; я знаю об этом, и я хотел бы сосредоточить обсуждение на основной проблеме):
// File "base.h"
#include <string>
#include "singleton.h"
#include "object_factory.h"
class Base;
using SingletonBaseFactory = Singleton
<
ObjectFactory
<
Base, // Abstract product type
std::string, // Identifier type
Base* (*)() // Concrete product creator type
>
>;
class Base {
// Define the interface (virtual functions, virtual dtor)
public:
// Wrap the Factory method
static Base* Factory(const std::string& ID) {
return SingletonBaseFactory::Instance().Factory(ID);
}
};
// File "derived_1.h"
#include "base.h"
class Derived_1 : public Base { /* ... */ };
// File "derived_2.h"
#include "base.h"
class Derived_2 : public Base { /* ... */ };
и зарегистрируйте каждый производный класс в анонимном пространстве имен в соответствующем файле реализации:
// File "derived_1.cpp"
#include "derived_1.h"
namespace {
Base* CreateDerived_1() {
return new Derived_1;
}
const bool registered = SingletonBaseFactory::Instance().Register("Derived_1", CreateDerived_1);
}
// Same for Derived_2 in file "derived_2.cpp"
Следовательно, пользователь, который хочет использовать эту иерархию в своем коде, должен просто вызвать метод Base::Factory
с правильным идентификатором:
// File main.cpp
#include"base.h"
int main(){
Base* pb = Base::Factory("Derived_1");
// Do stuff with pb
return 0;
}
Теперь предположим, что у меня есть иерархия классов шаблонов, скажем:
// File "baset.h"
#include <string>
#include "singleton.h"
#include "object_factory.h"
template<class T>
class BaseT;
template<class T>
using SingletonBaseTFactory = Singleton
<
ObjectFactory
<
BaseT<T>, // Abstract product type
std::string, // Identifier type
BaseT<T>* (*)() // Concrete product creator type
>
>;
template<class T>
class BaseT {
/*Define the interface*/
public:
BaseT Factory(const std::string& ID) {
return SingletonBaseTFactory<T>::Instance().Factory(ID);
}
};
// File "derivedt_1.h"
#include "baset.h"
template<class T>
class DerivedT_1 : public BaseT<T> { /* ... */ };
В этом случае регистрация является обязанностью пользователя для каждого типа T, который он хочет использовать, перед использованием иерархии классов:
// File main.cpp
#include "baset.h"
#include "derivedt_1.h"
bool register_derived_1_int = SingletonBaseTFactory<int>::Instance().Register("Derived_1", [](){ return new DerivedT_1<int>; });
int main() {
BaseT<int>* pb = BaseT<int>::Factory("Derived_1");
return 0;
}
Имея в виду, что идентификатор каждого производного шаблонного класса одинаков для каждого типа T
, имело бы ли смысл делегировать ответственность за регистрацию разработчику каждого производного класса (а не пользователю) даже в шаблонной форме? дело?
Если да, то есть ли обходной путь для его достижения?
EDIT:
Я нашел эту работу https://www.artima.com/cppsource/subscription_problem.html, которая решает проблему регистрации фабрики объектов в шаблонном случае.
Однако за регистрацию производных классов все еще отвечает тот, кто знает тип (типы), с которыми может быть создан производный класс.
Как правило, у разработчика классов нет такого знания - у пользователя оно есть.
Итак, опять же, есть ли способ отстранить пользователя от ответственности за регистрацию производного класса?