Archives

  • Categories

  • Refactoring Switch Statements With StructureMap

    Switch statements are commonly regarded as code smells, especially when the number of switch statements becomes very large. For this example I’m going to show you the traditional approach to refactor out a switch statement in C# and how you can further refine this approach using StructureMap though this would be possible using any IOC container.

    Consider the following switch statement which I’ve kept fairly simple for the purpose of  demonstration:

    public void ProcessCommand(Command command)
            {
                switch (command)
                {
                    case Command.Go:
                        Console.WriteLine("Go");
                        break;
    
                    case Command.Pause:
                        Console.WriteLine("Pause");
                        break;
    
                    case Command.Stop:
                        Console.WriteLine("Stop");
                        break;
    
                    default:
                        throw new Exception("Unhandled Command");
                }
            }
    

    The Classic Approach To Refactoring Switch Statements

    Normally when refactoring a switch statement we would perform the following steps:

    1) Create an abstract/interface for a handler e.g.

    public interface ICommandHandler
    {
        void Handle();
    }
    

    2) For each case statement create a concrete implementation of our abstract handler  e.g.

     public class GoHandler:ICommandHandler
        {
            public void Handle()
            {
                Console.WriteLine("Go");
            }
    
            public bool Handles(Command type)
            {
                return Command.Go == type;
            }
        }
    

    3) Create a Dictionary of our handlers using our switch statement key for our dictionary key e.g.

        public class RefactoredWithDictionary
        {
            private readonly Dictionary _commandHandlers;
    
            public RefactoredWithDictionary()
            {
                _commandHandlers = new Dictionary
                                       {
                                           {Command.Go, new GoHandler()},
                                           {Command.Pause, new PauseHandler()},
                                           {Command.Stop, new StopHandler()}
                                       };
            }
    
            public void DoSomethingWithTheSwitchStatement(Command command)
            {
                _commandHandlers[command].Handle();
            }
        }
    

    Ok this is a big improvement from our previous switch statement it helps us get a bit closer to our S.O.L.I.D code Nirvana by adhering to our single responsibility principle and encapsulating our logic in specific handlers. However if we need to add a new handler we will need to modify the code by adding a new handler to the dictionary which violates our Open Closed Principle.

    StructureMap To The Rescue!

    The first thing we need to do is modify our ICommandHandler interface slightly.

    public interface ICommandHandler
    {
            void Handle();
            bool Handles(Command command);
            bool IsDefault { get; }
    }
    

    We have added two methods to the Interface. The new Handles method means each concrete implementation can now return whether or not it can handle a specific Command and the IsDefault property tells us whether or not our Handler is the default handler or not.

    Our concrete implementation would now look like this:

        public class GoHandler:ICommandHandler
        {
            public void Handle()
            {
                Console.WriteLine("Go");
            }
    
            public bool Handles(Command type)
            {
                return Command.Go == type;
            }
    
            public bool IsDefault
            {
                get { return false; }
            }
        }
    

    Now our class that can take an IEnumerable collection of  ICommandHandlers and query the collection for a handler that can handle the particular command.  It can also query the collection for the default handler if there is no handler defined for the particular Command.

    Our class that originally contained the switch statement now looks like this:

        public class RefactoredWithStructureMap
        {
            private readonly IEnumerable _commandHandlers;
    
            public RefactoredWithStructureMap(IEnumerable commandHandlers)
            {
                _commandHandlers = commandHandlers;
            }
    
            public void DoSomethingWithTheSwitchStatement(Command command)
            {
                var defaultHandler = _commandHandlers.Where(handler => handler.IsDefault).First();
    
                var commandHandler = _commandHandlers
                    .Where(handler => handler.Handles(command))
                    .DefaultIfEmpty(defaultHandler)
                    .FirstOrDefault();
    
                commandHandler.Handle();
            }
        }
    

    Great but we need to make one more change so that we can get StructureMap to inject in our Handlers. We Need to modify our StructureMap Registry to do two things. Firstly we need to tell StructureMap to load all implementations of ICommandHandler. Secondly we need to tell StructureMap how to satisfy any requests for IEnumerable<ICommandHandler> and how to construct that collection.

    To do this we need to add the following lines to our Registry.

        internal class StructureMapRegistry : Registry
        {
            public StructureMapRegistry()
            {
                Scan(scanner =>
                         {
                             scanner.TheCallingAssembly();
                             scanner.WithDefaultConventions();
                             scanner.AddAllTypesOf();
                         });
    
                ForRequestedType>()
                    .TheDefault.Is.ConstructedBy(x => ObjectFactory.GetAllInstances());
            }
        }
    

    The beauty of this approach is now if we need to add a new handler all we have to do is write a new handler and it will automatically be injected into our class by StructureMap!

    kick it on DotNetKicks.com

    Tags: ,

    Leave a Reply