Create a derived class in the base class based on the parameter

My question is more or less identical to the one in Need a design template for deleting enumerations and a switch statement when creating an object However, I do not see the abstract factory work well here.

I am currently planning on refactoring / reimplementing some existing DAL / ORM mixing library. Somewhere in the existing code, there is code that looks like this:

class Base { static Base * create(struct Databasevalues dbValues) { switch(dbValues.ObjectType) { case typeA: return new DerivedA(dbValues); break; case typeB: return new DerivedB(dbValues); break; } } } class DerivedA : public Base { // ... } class DerivedB : public Base { // ... } 

Thus, the library responsible for communication with the database fills the structure with all the information about the essence of the database, and then the create () method is called, which actually creates the corresponding object in ORM. But I don't like the idea of ​​a base class that knows all its derived classes, and I also don't like the switch statement. I would also like to avoid creating another class just for the purpose of creating these objects. What do you think of the current approach? How to implement this functionality?

+4
source share
4 answers

No matter what you do, you will need either a switch or some other design that just hides the same logic.

However, what you can and should do is remove the creation method from your database - you are absolutely right, he should not know about its derivatives. This logic belongs to another object, such as a factory or controller.

+1
source

It has been discussed here millions of times. If you do not want to create a separate factory class, you can do this.

 class Base { public: template <class T> static void Register (TObjectType type) { _creators[type] = &creator<T>; } static Base* Create (TObjectType type) { std::map <TObjectType, Creator>::iterator C = _creators.find (type); if (C != _creators.end()) return C->second (); return 0; } private: template <class T> static Base* creator () { return new T; } private: typedef Base* (::*Creator) (); static std::map <TObjectType, Creator> _creators; }; int main () { Base::Register <Derived1> (typeA); Base::Register <Derived2> (typeB); Base* a = Base::Create (typeA); Base* b = Base::Create (typeB); } 
+6
source

Let's say you replace the switch with a map, for example map<ObjectType, function<Base* (DatabaseValues&)>> .

Now the factory (which may or may not live in the base class), you do not need to know about all subclasses.

However, the map needs to be populated somehow. This means that either something fills it (so your knowledge about all the problems of subclasses was simply pushed from one place to another), or you need subclasses to use static initialization to register their factory functions on the map.

+3
source

Just do not use enumerations. They are not an OO construct, so JAVA did not exist at the beginning (unfortunately, the pressure was too great to add them).

Consider instead of such an enumeration:

 enum Types { typeA, typeB }; 

this design, which does not need to be switched (another design without OO, in my opinion) and maps:

types.h

 class Base; class BaseFactory { public: virtual Base* create() = 0; }; class Types { public: // possible values static Types typeA; static Types typeB; // just for comparison - if you do not need - do not write... friend bool operator == (const Types & l, const Types & r) { return l.unique_id == r.unique_id; } // and make any other properties in this enum equivalent - don't add them somewhere else Base* create() { return baseFactory->create(); } private: Types(BaseFactory* baseFactory, unsigned unique_id); BaseFactory* baseFactory; unsigned unique_id; // don't ever write public getter for this member variable!!! }; 

Types.cpp

 #include "Types.h" #include "Base.h" #include "TypeA.h" #include "TypeB.h" namespace { TypeAFactory typeAFactory; TypeBFactory typeAFactory; unsigned unique_id = 0; } Types Types::typeA(&typeAFactory, unique_id++); Types Types::typeA(&typeBFactory, unique_id++); 

So your example (if you really need this function):

 class Base { static Base * create(struct Databasevalues dbValues) { return dbValues.ObjectType.create(); } }; 

Missing parts should be easy to implement.

+1
source

Source: https://habr.com/ru/post/1444861/


All Articles