Refactor large switch statement using Strategy pattern

In general I do not like switch-statements at all, but sometimes they just end up in the code anyway. Normally that is because of some deadline and the easiest way to solve the problem was so use a switch-statement. In my mind the code looks messy when I use a switch-statement. They also tend to grow in size. When I then have the time to refacor the code and see the switch, I really wanted to do something with it. After been googling a little bit I see that some people use the Strategy Design Pattern insted. I have to say that I do not know/remember what the strategy pattern is. This blog post is then my learning of refactoring a large switch-statement using the Strategy Design Pattern.

What is the Strategy Design Pattern?

The strategy pattern (also known as the policy pattern) is a behavioral software design pattern that enables selecting an algorithm at runtime. Instead of implementing a single algorithm directly, code receives run-time instructions as to which in a family of algorithms to use. Wikipedia

My problem

I have a method that recives messages and the messages have a property that tells me what kind of message it is.

public void HandleMessage(string messageType)
{
    switch(messageType)
    {
        case "MessageA":
            DoSomethingA();
            break;
        case "MessageB":
            DoSomethingB();
            break;
    }
}

The kind of messages was growing and just extending the switch-statement just feels wrond. That is when I started searching around for possible better ways of handling all the types of messages needed to be handled. After reading about Strategy Design Pattern I am convinst that it is a good solution for this kind of problem.

My solution using the Strategy Design Pattern

public interface IStructure
{
    void Exeute(string messageId);
    
    bool ShouldExecute(string messageId); 
}

First thing todo when implementing the strategy pattern is to create an interface for the strategy. in the example above, I have one method returning a boolean that just says if it should be running or not depending on the messageId input parameter. The there is the exeute method itself.

public class MessageAStrategy : IStructure
{
    const string MessageType = "MessageTypeA";
        
    public void Execute()
    {
        // Do what that needs to be done with message A
    }
    
    public bool ShouldExecute(string messageId)
    {
        return messageId.Equals(MessageType, StringComparison.CurrentCultureIgnoreCase);
    }
}

public class MessageBStrategy : IStructure
{
    const string MessageType = "MessageTypeA";
    
    public void Execute()
    {
        // Do what that needs to be done with message B
    }
    
    public bool ShouldExecute(string messageId)
    {
        return messageId.Equals(MessageType, StringComparison.CurrentCultureIgnoreCase);
    }
}

First of all, all the strategies implement the strategy interface. Then all the implementations need to implement the CanExecute method. In my implementation I have a const string value telling me for which message type this implementation should be used.

public class MessageContext
{
    private readonly IEnumerable<IStrategy> _strategies;

    public MessageContext(IEnumerable<IStrategy> stragegies)
    {
        _strategies = strategies;
    }
    
    public void ExecuteMessageAction(string messageId)
    {
        var action = _strategies.FirstOrDefault(w => w.ShouldExecute(messageId));
        
        if(action == null) throw new Exception("Could not handle message");
        
        action.Execute();
    }
}

The last thing to get the strategy design pattern into use is to have a context. The context is what is going to be called from the place where the messages are coming in to make sure that the right implementation based on the message type is used. All implementations are dependency injected into an IEnumerable, then there is a simple Linq query to pick the right implementation.

I Have used Autofac for several yeaers now, but did not use more than the basic simple features. I had never thought of using reflection to regiter all the implementations of an interface. I found great help when I found this blog post that showed that.

builder.RegisterAssemblyTypes(Assembly.Load("MyAssesmbly")).Where(t => typeof(IStrategy).IsAssignableFrom(t)).InstancePerLifetimeScope().AsImplementedInterfaces();

When the IStrategy interface is registered, then it is possible to use IEnumerable<IStrategy> in the constructor. This will give you an IEnumerable of all the instances that implement the IStrategy interface in the assembly named MyAssembly.

Conclusion

I am really happy that I took the time to refactor that big switch-statement since the readability of the code become much easier. When it is needed to add a new message type to the system, I really just need to create a new class that extends from the IStrategy interface then it will work from the beginning.

Links

Teis Lindemark

Software developer, beer brewer and AGENT backer

Bergen, Norway https://teilin.net

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer’s view in any way.