Click here to Skip to main content
Click here to Skip to main content

Refactoring a Switch Statement

By , 15 Aug 2009
 

Introduction

This article describes refactoring a switch statement in order to reduce Cyclomatic complexity.

Background

Many developers would have scratched their heads in order to keep their Cyclomatic complexity under 10. In some cases, it is really not possible at first site. In this article, I have provided a way to do that. Take the example of a Device State machine. You have a number of device states as below:

public enum DeviceStatesEnum
{
    PowerUp,
    Waiting,
    StandBy,
    Inactive,
    Active,
    Start,
    Ready,
    Equilibrating,
    StartRunning,
    Running,
    ShoutingDown,
    ShoutDown,
    WarmingUp,
    Error
}

Using the code

Just download the code and run it.

public enum DeviceStatesEnum
{
    PowerUp,
    Waiting,
    StandBy,
    Inactive,
    Active,
    Start,
    Ready,
    Equilibrating,
    StartRunning,
    Running,
    ShoutingDown,
    ShoutDown,
    WarmingUp,
    Error
}

You have come up with the following class in order to handle the various states:

public class DeviceController
{
   private DeviceStatesEnum _currentDeviceState = DeviceStatesEnum.ShoutDown;

   public void ChangeDeviceState(DeviceStatesEnum deviceState)
   {
       switch (deviceState)
       {
           case DeviceStatesEnum.Active:
                SetDeviceStateToActive();
                break;
           case DeviceStatesEnum.Equilibrating:
                SetDeviceStateToEquilibrating();
                break;
           case DeviceStatesEnum.Error:
                SetDeviceStateToError();
                break;
           case DeviceStatesEnum.Inactive:
                SetDeviceStateToInactive();
                break;
           case DeviceStatesEnum.PowerUp:
                SetDeviceStateToPowerUp();
                break;
           case DeviceStatesEnum.Ready:
                SetDeviceStateToReady();
                break;
           case DeviceStatesEnum.Running:
                SetDeviceStateToRunning();
                break;
           case DeviceStatesEnum.ShoutDown:
                SetDeviceStateToShoutDown();
                break;
           case DeviceStatesEnum.ShoutingDown:
                SetDeviceStateToShoutingDown();
                break;
           case DeviceStatesEnum.StartRunning:
                SetDeviceStateToStartRunning();
                break;
       }
   }

   public DeviceStatesEnum GetDeviceState()
   {
       return _currentDeviceState;
   }

   private void SetDeviceStateToStartRunning()
   {
       if(_currentDeviceState == DeviceStatesEnum.Ready)
       _currentDeviceState = DeviceStatesEnum.StartRunning;
   }

   private void SetDeviceStateToShoutingDown()
   {
       _currentDeviceState = DeviceStatesEnum.ShoutingDown;
   }

   private void SetDeviceStateToShoutDown()
   {
       _currentDeviceState = DeviceStatesEnum.ShoutDown;
   }

   private void SetDeviceStateToRunning()
   {
       if (_currentDeviceState == DeviceStatesEnum.StartRunning )
       {
           _currentDeviceState = DeviceStatesEnum.Running;
       }
   }

   private void SetDeviceStateToReady()
   {
       if (_currentDeviceState == DeviceStatesEnum.Equilibrating )
       {
           _currentDeviceState = DeviceStatesEnum.Ready;
       }
   }

   private void SetDeviceStateToPowerUp()
   {
       if (_currentDeviceState != DeviceStatesEnum.Error)
           _currentDeviceState = DeviceStatesEnum.PowerUp;
   }

   private void SetDeviceStateToInactive()
   {
       if(_currentDeviceState != DeviceStatesEnum.Error)
       _currentDeviceState = DeviceStatesEnum.Inactive;
   }

   private void SetDeviceStateToError()
   {
       _currentDeviceState = DeviceStatesEnum.Error;
   }

   private void SetDeviceStateToEquilibrating()
   {
       if (_currentDeviceState == DeviceStatesEnum.Active)
       {
           _currentDeviceState = DeviceStatesEnum.Equilibrating;
       }
   }

   private void SetDeviceStateToActive()
   {
       if (_currentDeviceState != DeviceStatesEnum.Error)
       {
           _currentDeviceState = DeviceStatesEnum.Active;
       }
   }
}

Since you have 14 states, you will end up having a Cyclomatic complexity more than 14. Here is the way to reduce this complexity to 1. Do the following changes in the above class DeviceController. Declare a Dictionary.

private Dictionary<DeviceStatesEnum, Action> _deviceStateHandler = 
                                new Dictionary<DeviceStatesEnum, Action>();

Add a constructor to create a mapping with the states.

public DeviceController()
{
   _deviceStateHandler.Add(DeviceStatesEnum.Active, new Action(SetDeviceStateToActive));
   _deviceStateHandler.Add(DeviceStatesEnum.Equilibrating, 
                           new Action(SetDeviceStateToEquilibrating));
   _deviceStateHandler.Add(DeviceStatesEnum.Error, new Action(SetDeviceStateToError));
   _deviceStateHandler.Add(DeviceStatesEnum.Inactive, 
                           new Action(SetDeviceStateToInactive));
   _deviceStateHandler.Add(DeviceStatesEnum.PowerUp, new Action(SetDeviceStateToPowerUp));
   _deviceStateHandler.Add(DeviceStatesEnum.Ready, new Action(SetDeviceStateToReady));
   _deviceStateHandler.Add(DeviceStatesEnum.Running, new Action(SetDeviceStateToRunning));
   _deviceStateHandler.Add(DeviceStatesEnum.ShoutDown, 
                           new Action(SetDeviceStateToShoutDown));
   _deviceStateHandler.Add(DeviceStatesEnum.ShoutingDown, 
                           new Action(SetDeviceStateToShoutingDown));
   _deviceStateHandler.Add(DeviceStatesEnum.StartRunning, 
                           new Action(SetDeviceStateToActive));
   
   //TODO
   //Create mapping for all the states
}

Then, change the ChangeDeviceState as below:

public void ChangeDeviceState(DeviceStatesEnum deviceState)
{
   _deviceStateHandler[deviceState].Invoke();
}

Now the Cyclomatic complexity of the method ChangeDeviceState is one.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

rajeshjj
Software Developer (Senior) MphasiS an EDS company
India India
Member
I am dotnet programmer

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Questionhow to create the dictonary when you have parameters to the functionmemberMember 245440012 Aug '12 - 21:44 
same is the scenario, i want to refactor the switch statement
but my functions take 2 parameters and return bool.
So the question is in this case how to pass the parameters to the function
and how to call it.
 
thanks for any help.
GeneralThe Idea remains important!memberUrs Enzler18 Aug '09 - 3:16 
Although your sample may not be the best (as written in other comments), I think it's important that such switch cases have to be eliminated.
Either with a problem specific solution (like you did it, however I'd prefer using an existing state machine implementation like the one in bbv.Common[^]) or with polymorphism.
 
Cheers,
Urs
 
-^-^-^-^-^-^-^-
planetgeek.ch

GeneralMy vote of 2memberBigTuna17 Aug '09 - 10:20 
This article is for beginners yet there's no explanation of Cyclomatic Complexity. Also shows complete lack of respect for commenters by copy-pasting the same response.
GeneralVote 3memberWillemToerien17 Aug '09 - 0:28 
At least you know something about how bad a case is. I would prefer a state pattern for this. Why not just add a state pattern as well?
 
Assumption is the mother of all f*ck ups.

GeneralRe: Vote 3memberrajeshjj17 Aug '09 - 5:26 
You are right. This sample is not for explaining state pattern. It is another way to refractor switch case.
 
Unless you have complex state transition logic or other complex logic, I won’t prefer state pattern. For simple logic and all if you use the static pattern, we will end with class explosion.
 
What I have explained in the article is kind of strategic pattern.
Any way thanks for you comment.
 
Plseae accept

GeneralDon't like itmemberDetoX8316 Aug '09 - 9:57 
-You reduced the complexity - yes .. but the example is not good what so ever. State pattern should go here!
GeneralRe: Don't like itmemberrajeshjj17 Aug '09 - 5:28 
You are right. This sample is not for explaining state pattern. It is another way to refractor switch case.
 
Unless you have complex state transition logic or other complex logic, I won’t prefer state pattern. For simple logic and all if you use the static pattern, we will end with class explosion.
 
What I have explained in the article is kind of strategic pattern.
Any way thanks for you comment
 
Plseae accept

GeneralA questionmemberGio Bejarasco15 Aug '09 - 11:00 
What made you opt for this over State pattern?
GeneralRe: A questionmemberrajeshjj17 Aug '09 - 5:26 
You are right. This sample is not for explaining state pattern. It is another way to refractor switch case.
 
Unless you have complex state transition logic or other complex logic, I won’t prefer state pattern. For simple logic and all if you use the static pattern, we will end with class explosion.
 
What I have explained in the article is kind of strategic pattern.
Any way thanks for you comment.
 
Plseae accept

GeneralHere is something similar ;)memberSharpoverride15 Aug '09 - 9:30 
http://www.lostechies.com/blogs/sean_chambers/archive/2009/08/11/refactoring-day-11-switch-to-strategy.aspx[^]
 
Lazar Mihai

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130523.1 | Last Updated 15 Aug 2009
Article Copyright 2009 by rajeshjj
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid