Design scheme for refactoring operator

I have something like the following in the title

class MsgBase { public: unsigned int getMsgType() const { return type_; } ... private: enum Types { MSG_DERIVED_1, MSG_DERIVED_2, ... MSG_DERIVED_N }; unsigned int type_; ... }; class MsgDerived1 : public MsgBase { ... }; class MsgDerived2 : public MsgBase { ... }; ... class MsgDerivedN : public MsgBase { ... }; 

and used as

 MsgBase msgHeader; // peeks into the input stream to grab the // base class that has the derived message type // non-destructively inputStream.deserializePeek( msgHeader ); unsigned int msgType = msgHeader.getMsgType(); MsgDerived1 msgDerived1; MsgDerived2 msgDerived2; ... MsgDerivedN msgDerivedN; switch( msgType ) { case MSG_DERIVED_1: // fills out msgDerived1 from the inputStream // destructively inputStream.deserialize( msgDerived1 ); /* do MsgDerived1 processing */ break; case MSG_DERIVED_2: inputStream.deserialize( msgDerived2 ); /* do MsgDerived1 processing */ break; ... case MSG_DERIVED_N: inputStream.deserialize( msgDerivedN ); /* do MsgDerived1 processing */ break; } 

This seems like a type of situation that would be fairly common and well suited for refactoring. What would be the best way to apply design patterns (or a basic redesign of a C ++ language) to reorganize this code?

I read that the command template is usually used for refactoring operators, but this seems to be applicable only when choosing between algorithms to complete a task. Is this a place to apply a factory pattern or an abstract factory (I am not very familiar too)? Double dispatch?

I tried to eliminate as much of the irrelevant context as possible, but if I missed something important, just let me know and I will edit it to include it. Also, I couldn't find anything like that, but if it's a duplicate, just redirect me to the appropriate SO question.

+6
c ++ design-patterns refactoring
source share
4 answers

Extract types and type_s from MsgBase, they do not belong there.

If you want a complete fantasy, register all of your derived types with a factory along with a token (such as a β€œtype”) that the factory will use to know what to do. The factory then looks at this token during deserialization in its table and creates the correct message.

 class DerivedMessage : public Message { public: static Message* Create(Stream&); bool Serialize(Stream&); private: static bool isRegistered; }; // sure, turn this into a macro, use a singleton, whatever you like bool DerivedMessage::isRegistered = g_messageFactory.Register(Hash("DerivedMessage"), DerivedMessage::Create); 

etc .. The Create static method selects a new DerivedMessage and deserializes it, the Serialize method writes a token (in this case, Hash("DerivedMessage") ), and then it serializes. One of them should probably check that it is registered so that it is not lost from the linker.

(Remarkably, this method does not require an enumeration or other β€œstatic list of everything that ever exists.” Currently, I cannot come up with another method that does not require circular references to some extent.)

+2
source share

You can use the Factory Method template, which creates the correct implementation of the base class (derived class) based on the value that you are viewing from the stream.

+5
source share

The switch is not so bad. This is one way to implement the factory pattern. It is easy to test, it makes it easy to understand the full range of available objects and is well suited for testing coverage.

Another method is to create a mapping between enumeration types and factories to create specific objects from the data stream. This turns the compilation timer into a temporary search. A mapping can be created at run time, which allows you to add new types without recompiling everything.

 // You'll have multiple Factories, all using this signature. typedef MsgBase *(*Factory)(StreamType &); // For example: MsgBase *CreateDerived1(StreamType &inputStream) { MsgDerived1 *ptr = new MsgDerived1; inputStream.deserialize(ptr); return ptr; } std::map<Types, Factory> knownTypes; knownTypes[MSG_DERIVED_1] = CreateDerived1; // Then, given the type, you can instantiate the correct object: MsgBase *object = (*knownTypes[type])(inputStream); ... delete object; 
+3
source share

It is usually a bad idea for a base class to have knowledge of derived classes, so redesign is definitely fine. A sample factory is probably what you want here, as you already noted.

+1
source share

All Articles