You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
If in the future I need to introduce new Message classes, like MessageD, the if/else if block will continue to grow, and I will need to add new cases, increasing the risk of introducing bugs.
Multiple Type Casting/Checking:
(message is MessageA) and other similar: There are 2 problems here, repeated type checks and hard casting
(messageA?.): repeatedly null checks for each specific type
Maintainability
The message processing logic is centralised; if this feature grows larger, it will become harder to manage, as more message types and methods are added.
Design Patterns and Design Principles:
The use of a large sequence of if statements is a code smell and could be classified as a Switch Statements Anti-Pattern
The code violates the Open/Closed Principle (OCP) principle
My alternative suggestion
Code Refactoring, implementing Polymorphism and Strategy Pattern
Step 1: Defining an IMessage Interface, because all message types have a common action
public interface IMessage
{
// The method that implements the common actions between the message types
void Process();
}
Step 2: Now, each message class implements the IMessage interface and moves its specific logic into the Process() method
public class MessageA : IMessage
{
public void Process()
{
// Equivalent to: messageA?.MyCustomMethodOnA();
MyCustomMethodOnA();
}
public void MyCustomMethodOnA() { /* ... */ }
}
public class MessageB : IMessage
{
public void Process()
{
// Equivalent to: messageB?.MyCustomMethodOnB();
// Equivalent to: messageB?.SomeAdditionalMethodOnB();
MyCustomMethodOnB();
SomeAdditionalMethodOnB();
}
public void MyCustomMethodOnB() { /* ... */ }
public void SomeAdditionalMethodOnB() { /* ... */ }
}
public class MessageC : IMessage
{
public void Process()
{
// Equivalent to: messageC?.MyCustomMethodOnC();
MyCustomMethodOnC();
}
public void MyCustomMethodOnC() { /* ... */ }
}
Step 3: Now, independently of the number of existing type messages, the code doesn’t change and provides a solution in just one line
public void HandleMessage(IMessage message)
{
message.Process();
}
Bonus: Or, it could also be used like this
public class SendMessage
{
private readonly IMessage _message();
public SendMessage(IMessage message)
{
_message = message;
}
public void HandleMessage()
{
/* ... */
_message.Process()
/* ... */
}
}
//
_sendMessage(new MessageA())
//
_sendMessage(new MessageB())
//
_sendMessage(new MessageC())
Improvements (Conclusion)
Now, with these changes, we have implemented the OCP Principle:
If I need to add a new MessageD or more, I can just create a class that implements IMessage and put its logic in Process() without changing the HandleMessage.
In addition to the OCP Principle, we are following the Single Responsibility Principle, because each message class implements its own responsibility.
The code is now clean, decoupling, easy to maintain, and testable.
Answer: The quality of this code is low.
Issues that cause low code quality:
If in the future I need to introduce new Message classes, like
MessageD, theif/else ifblock will continue to grow, and I will need to add new cases, increasing the risk of introducing bugs.Multiple Type Casting/Checking:
message is MessageA) and other similar: There are 2 problems here, repeated type checks and hard castingmessageA?.): repeatedly null checks for each specific typeMaintainability
The message processing logic is centralised; if this feature grows larger, it will become harder to manage, as more message types and methods are added.
Design Patterns and Design Principles:
The use of a large sequence of if statements is a code smell and could be classified as a Switch Statements Anti-Pattern
The code violates the Open/Closed Principle (OCP) principle
My alternative suggestion
Code Refactoring, implementing Polymorphism and Strategy Pattern
Step 1: Defining an IMessage Interface, because all message types have a common action
Step 2: Now, each message class implements the
IMessageinterface and moves its specific logic into theProcess()methodStep 3: Now, independently of the number of existing type messages, the code doesn’t change and provides a solution in just one line
Bonus: Or, it could also be used like this
Improvements (Conclusion)
Now, with these changes, we have implemented the OCP Principle:
If I need to add a new
MessageDor more, I can just create a class that implementsIMessageand put its logic inProcess()without changing theHandleMessage.In addition to the OCP Principle, we are following the Single Responsibility Principle, because each message class implements its own responsibility.
The code is now clean, decoupling, easy to maintain, and testable.