Click here to Skip to main content
15,881,172 members
Articles / Programming Languages / C#

Refactoring a Switch Statement

Rate me:
Please Sign up or sign in to vote.
3.33/5 (7 votes)
15 Aug 2009CPOL 56.3K   317   14   11
Avoiding a switch statement in order to avoid Cyclomatic complexity.

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:

C#
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.

C#
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:

C#
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.

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

Add a constructor to create a mapping with the states.

C#
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:

C#
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)


Written By
Software Developer (Senior) MphasiS an HP company
India India
I am dotnet programmer

Comments and Discussions

 
Questionhow to create the dictonary when you have parameters to the function Pin
Member 245440012-Aug-12 21:44
Member 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! Pin
Urs Enzler18-Aug-09 3:16
Urs Enzler18-Aug-09 3:16 
GeneralMy vote of 2 Pin
BigTuna17-Aug-09 10:20
BigTuna17-Aug-09 10:20 
GeneralVote 3 Pin
WillemToerien17-Aug-09 0:28
WillemToerien17-Aug-09 0:28 
GeneralRe: Vote 3 Pin
rajeshjj17-Aug-09 5:26
rajeshjj17-Aug-09 5:26 
GeneralDon't like it Pin
Hakger16-Aug-09 9:57
Hakger16-Aug-09 9:57 
GeneralRe: Don't like it Pin
rajeshjj17-Aug-09 5:28
rajeshjj17-Aug-09 5:28 
GeneralA question Pin
Giovanni Bejarasco15-Aug-09 11:00
Giovanni Bejarasco15-Aug-09 11:00 
GeneralRe: A question Pin
rajeshjj17-Aug-09 5:26
rajeshjj17-Aug-09 5:26 
GeneralHere is something similar ;) Pin
Sharpoverride15-Aug-09 9:30
Sharpoverride15-Aug-09 9:30 
GeneralRe: Here is something similar ;) Pin
emiaj17-Aug-09 6:28
emiaj17-Aug-09 6:28 

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

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.