Applying Strategy Pattern Instead of Using Switch Statements
Applying Strategy Pattern Instead of Using Switch Statements
Once in a while I’m stumbling on switch statements during a
Code Review session. Whenever this is happening my first reaction
is to understand why did the developer use it.
Since using switch statement sometime implies spaghetti code and
also can get very crowded (in case statements of course) in this post
I’m going to show an alternative method that I prefer to use.
Alternative Method for Switch Statements
Whenever you start to use a switch statement you should
ask yourself whether you can use Strategy Pattern instead.
The Strategy Pattern help us to divide an algorithm from a host class
and then move it to another class. By doing so the client can choose
which algorithm will be performed in runtime from a set of algorithms
that were implemented earlier.
The Strategy Pattern can also help us when we want to replace a
switch statement. We can look at every case as a different
algorithm that we want to use which differ only by some condition
or a key. Since this is the case, for every case we can build a class that
do an action (the algorithm) and by that we can prevent the horror of
using an “endless” switch or spaghetti code.
Applying Strategy Pattern Over a Switch Statement
The following code shows how to replace a switch statement with
a Strategy implementation. We start with the first implementation
before we refactor it:
class Program
{ public enum ePassengerTitle
{ Mr,
Mrs,
Doctor,
}
static void Main(string[] args)
{ ePassengerTitle title = ePassengerTitle.Doctor;
switch (title)
{ case ePassengerTitle.Mr:
// do something
break;
case ePassengerTitle.Mrs:
// do something
break;
case ePassengerTitle.Doctor:
// do something
break;
default:
break;
}
}
}
As you can see I have an enum which holds a passenger title.
This title can get crowded for example when we want to add
more titles like Reverend, Miss or more.
Lets see how we can change this implementation to use the
Strategy Pattern:
#region Strategy Interface
public interface IPassengerTitleStrategy
{ void DoAlgorithm();
}
#endregion
#region Concrete Classes
public class MrPassengerTitleStrategy : IPassengerTitleStrategy
{ #region IPassengerTitleStrategy Members
public void DoAlgorithm()
{ throw new NotImplementedException();
}
#endregion
}
public class MrsPassengerTitleStrategy : IPassengerTitleStrategy
{ #region IPassengerTitleStrategy Members
public void DoAlgorithm()
{ throw new NotImplementedException();
}
#endregion
}
public class DoctorPassengerTitleStrategy : IPassengerTitleStrategy
{ #region IPassengerTitleStrategy Members
public void DoAlgorithm()
{ throw new NotImplementedException();
}
#endregion
}
#endregion
#region Context
public class Context
{ #region Members
private static Dictionary<ePassengerTitle, IPassengerTitleStrategy> _strategies =
new Dictionary<ePassengerTitle, IPassengerTitleStrategy>();
#endregion
#region Ctor
public static Context()
{ _strategies.Add(ePassengerTitle.Mr, new MrPassengerTitleStrategy());
_strategies.Add(ePassengerTitle.Mrs, new MrsPassengerTitleStrategy());
_strategies.Add(ePassengerTitle.Doctor, new DoctorPassengerTitleStrategy());
}
#endregion
#region Methods
public static void DoAlgorithm(ePassengerTitle title)
{ _strategies[title].DoAlgorithm();
}
#endregion
}
#endregion
You can see that I register all the algorithms inside a dictionary
which will help us to imply the relevant algorithm whenever we
need it. The registration is done inside the Context class which
then exposes the DoAlgorithm method which gets the relevant
passenger title and implies to it the relevant algorithm.
Lets see how our program change when we use the Strategy
implementation:
public enum ePassengerTitle
{ Mr,
Mrs,
Doctor,
}
class Program
{ static void Main(string[] args)
{ ePassengerTitle title = ePassengerTitle.Doctor;
Context.DoAlgorithm(title);
}
}
This is only one way to implement the
Strategy Pattern. There are other
ways to do the same thing but without dictionaries which you can find in
the internet.
- The code is easier to read. I don’t need to go and read (or
search) an “endless” switch statement to understand each
aspect of the code.
- The code is more maintainable. I only need to go to the
relevant class that implement the algorithm in order to change it
or refactor it when needed.
- It is easier to add more algorithms. I only need to add more classes
to the pile of algorithms and that is it. Doing so helps us to imply
the Open / Closed Principle because in switch statement we are going
to have to change our code (add another case statement) in opposed
to Strategy which we add another algorithm class.
- Strategy Pattern is more testable.
Even so, there are times that switch statement is preferable then
Strategy Pattern but when you find yourself having an “endless” switch
or repeat the same switch all over the place this should alert you that
Strategy Pattern is needed.
Summary
In the post I showed an alternative method for switch statement use. I
always prefer the use of Strategy Pattern instead of switch statements.
This approach is more OOP for developing even though it will force you to
implement more classes. In the end you can choose either the switch
statement or Strategy Pattern but if you have chosen a switch statement
do it in the right place.