class IFactoryBasic { public: IFactoryBasic() {} virtual ~IFactoryBasic() {} virtual Test* create(const QString &key, const QString &args)=0; };
Test
is a certain base class class Test { protected: QString _word; public: Test(const QString &word):_word(word) {} virtual ~Test() {} virtual void test(){ qDebug()<<"test "<<_word; } }; class TestChild: public Test { public: TestChild(const QString &word): Test(word) {} virtual void test() { qDebug()<<"test child"<<_word; } };
TestChild
- Test
heirword
in the constructor, which we can then verify in the test()
function. template<class T, class C, class A> class IFactory { QMap<QString, C* (T::*)(const A&) > handler; protected: void add(const QString &key, C *(T::*func)(const A &)) { handler[key]=func; } public: IFactory() {} virtual ~IFactory() {} C *make(const QString &key, const A &args) { if(handler.contains(key)) { T* inheritor = dynamic_cast<T*>(this); if(inheritor) return (inheritor->*handler[key])(args); } return 0; } };
handler
IFactory
class IFactory
is an associative container containing a key and the corresponding object creation function. The signature of the spawning function is described as C* (T::*)(const A&)
, that is, the return value will have a pointer to a certain C
class, and the function argument is passed a reference to an object of type A
add(...)
function adds a key-function pair <key,func>
to the container.make(...)
calls the spawn function, if it exists in the container (after dynamically transforming the pointer type of this
to the type of the heir, otherwise you cannot call functions that were defined there). class FactoryFst: public IFactory<FactoryFst, Test, QString>, public IFactoryBasic { Test *createOrigin(const QString &args){ return new Test(args); } Test *createChild(const QString &args) { return new TestChild(args); } public: FactoryFst() { add("test", &FactoryFst::createOrigin); add("testchild", &FactoryFst::createChild); } Test *create(const QString &key, const QString &args) { return make(key, args); } };
IFactoryBasic
interface. For the other parent, we explicitly specify the heir of the FactoryFst
, the returned pointer will be a pointer to an object of the Test
class, and a reference to the QString
object is passed as an argument.Test
and TestChild
:Test *createOrigin(const QString &args){ return new Test(args); }
Test *createOrigin(const QString &args){ return new Test(args); }
- creates an object of type Test, passing the argument QString
to it in the constructor.Test *createChild(const QString &args) { return new TestChild(args); }
Test *createChild(const QString &args) { return new TestChild(args); }
- similarly creates an object of type TestChild
.FactoryFst
constructor and define the create(...)
IFactoryBasic
interface. class Arguments { public: virtual ~Arguments() {} }; template<class T> class TArguments: public Arguments { public: T arg; TArguments(T _arg):arg(_arg) {} };
Arguments
is the base class, TArguments
is the template class for storing the transmitted object.new
. class Container { public: virtual ~Container() {} virtual void *make( Arguments* ) = 0; };
Container
serves the same purpose as Arguments
. So that we could always call the spawning function make(...)
for any of its template heirs. The make(...)
function should return a pointer to the created object. template<class T, typename A> class TContainer: public Container { public: void *make(Arguments* args=0) { TArguments<A>* a = dynamic_cast< TArguments<A>* >( args ); if(!a) return 0; return new T(a->arg); } };
TContainer
class TContainer
already template, the type of the returned pointer T
and the type of the argument for the constructor A
passed to it as template arguments.make(...)
, the pointer to Arguments
is passed as an argument, but we understand that in fact it must be a pointer to TArguments and try to dynamically convert the type. If all conversions were successful, we can create an object of a previously defined type. template<typename C=void, typename A=void> class TemplateFactory { QMap<QString, Container*> handler; public: TemplateFactory() {} virtual ~TemplateFactory(){ qDeleteAll(handler.values()); } template<class T> void add(const QString &name) { handler.insert(name, new TContainer<T, A>()); } C *make(const QString &name, const A &arg) { if(handler.contains(name)) return static_cast<C*>(handler.value(name)->make(new TArguments<A>(arg))); return 0; } };
C
are the base class, A
is the argument of the constructor of the objects being generated.add(...)
function registers new classes in the list, make(...)
creates a class object, passing the function a key to select the type and the argument of the constructor to the function. Type static_cast
used to convert the type void*
to the desired C*
. class FactorySnd: public TemplateFactory<Test, QString>, public IFactoryBasic { public: FactorySnd() { add<Test>("test"); add<TestCild>("testchild"); } Test* create(const QString &name, const QString &arg){ return make(name, arg); } };
create(...)
function is overridden. In the constructor, classes are registered. IFactoryBasic* factory = new FactorySnd(); Test* test = factory->create("test", "A"); test->test(); test = factory->create("testchild", "B"); test->test(); delete factory; factory = new FactoryFst(); Test *stest = factory->create("test", "C"); stest->test(); stest = factory->create("testchild", "D"); stest->test();
test "A" test child "B" test "C" test child "D"
QObject)
heir QObject)
or register it with an Observer. You can also somehow modify the argument passed to the generating method before passing it to the constructor. But the charge for this is more complex code maintenance, the addition of a new generated object. The second method is less demanding in this respect, but leaves less freedom for the user. That is, the creation of an object occurs as described in the base class and nothing else. Also, factories are focused on creating objects inherited from one class and taking one object as an argument. In this initializer object, all necessary properties must be encapsulated to create a new object. This, of course, is not universal (it imposes certain restrictions on the objects being created), but it is quite suitable for solving an applied problem.Source: https://habr.com/ru/post/192520/
All Articles