Removing dependencies on subclass-specific behavior
https://softwareengineering.stackexchange.com/questions/233671
-
03-10-2020 - |
Question
I have a Message
class which can contain multiple types of payloads (or sometimes no payload), each derived from a common Payload
class. However, this becomes problematic because the Message
class wants to know about the Payload
subclasses for various reasons such as:
Checking for
Message
equalityif (message parts besides the payload are equal) { switch(type) { case Payload::Type::RESPONSE: return *static_cast<ResponsePayload*>(payload.get()) == *static_cast<ResponsePayload*>(o.payload.get()); break; case Payload::Type::SETUP: return *static_cast<SetupPayload*>(payload.get()) == *static_cast<SetupPayload*>(o.payload.get()); break; ... } }
Deserializing (since the deserialization methods are static because the payloads are immutable)
switch(type) { case Payload::Type::RESPONSE: load = ResponsePayload::fromJSON(payloadValue); break; case Payload::Type::SETUP: load = SetupPayload::fromJSON(payloadValue); break; ... case Payload::Type::START: case Payload::Type::STOP: case ...: break; // Load stays null default: THROW(Exception, "Error in program logic: we forgot to parse some payload"); }
Ensuring a
Payload
is attached to aMessage
while constructing it:switch(type) { case Payload::Type::RESPONSE: case Payload::Type::SETUP: case ...: ENFORCE(IOException, payload != nullptr, "For this message type, a payload is required."); break; case Payload::Type::START: case Payload::Type::STOP: case ...: ENFORCE(IOException, payload == nullptr, "For this message type, the payload should be null"); break; default: THROW(Exception, "Error in program logic: we forgot to handle some payload"); }
Alarm bells are going are going off in my head - this violates SOLID like there's no tomorrow and obviously doesn't scale well since I have to add case
statements every time I add a new payload. Is there a cleaner approach I could take?
Solution
Alarm bells should be going off. But some top of my head initial suggestions (meaning I didn't take the time to come up with the best solution, just a much better one than you've got).
1 - Why is there a separate Message and Payload class, aren't they the same thing?
2 - Include the equality check in the subclasses where they take a payload instance as the parameter. Subclasses only know about their own type. If you pass a payload that isn't the right type then the type conversion will fail and you know they aren't equal.
3 - Create a Payload factory instead of creating all the various types in the base message class.
4 - Subclass the Message class if it requires parameters to be built properly.