Applying Strategy Pattern Instead of Using Switch Statements

November 22, 2009

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.

What we gain by using Strategy Pattern?



  • 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. 


DotNetKicks Image

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

15 comments

  1. Guy ShvoronNovember 22, 2009 ב 14:25

    Excellent stuff and example,
    thanks.

    Reply
  2. TzachiNovember 22, 2009 ב 16:06

    Great example. in this case it is also calld “replace conditional with dispatcher”.

    Reply
  3. VinceNovember 22, 2009 ב 16:34

    I’d also like to point out that this approach would take longer for a developer to figure out whats going on.

    Complicating code just because it’s more ‘oo’ seems like the wrong approach.

    In the end, weather your adding another class or adding in a new case statement that calls a method is the same.

    Reply
  4. BryanNovember 22, 2009 ב 21:09

    “I’d also like to point out that this approach would take longer for a developer to figure out whats going on.

    Complicating code just because it’s more ‘oo’ seems like the wrong approach.

    In the end, weather your adding another class or adding in a new case statement that calls a method is the same.”

    Completely disagree.  The whole point of the pattern is to prevent enormous blocks of linear code.  OOD allows to to organize your logic more like “tree” of code rather than a linear listing.  The above example is rudimentary, but when you deal with classes that are 2000+ lines long, the amount of time required to digest the code is exponentially longer than the small up-front investment to learn the “framework”, in this case, the Strategy Pattern.  Switch statements are an excellent point of attack in such refactoring because they quickly become unmaintainable as business logic is added and added over time.  In the end, this pattern is a simple combination of interface mapping using enums for keys, so it should be very comfortable for experienced developers to digest.  For junior developers, perhaps the example could be simplified by using a Dictionary instance directly in the main block, but this wouldn’t emphasize the re-usability of the pattern as well.

    Reply
  5. Jeff DarcyNovember 22, 2009 ב 22:14

    So you’d replace a simple set of comparisons and/or a jump table (which is what a switch compiles down to) with a dictionary lookup and a method call on an object that wouldn’t otherwise need to exist?  You’d split even simple logic across several classes, making it vastly harder to follow even if they use your favorite IDE that lets you jump between them easily, just so you can stand up proudly in front of your pattern-addled friends and say it’s the Strategy Pattern?  I-N-S-A-N-E.

    There are some cases where this pattern does apply, most often when the same switch occurs many times – i.e. with the same cases leading to the same actions or nearly so.  However, that’s a small minority of cases.  Why not extend your “logic” to if-statements as well, with separate strategy objects for the true and false cases?  Because it would be an inefficient unreadable mess, with myriad strategy either requiring an unreasonable number of arguments or violating their callers’ encapsulation.  The same applies to your recommendation for switches.

    If your “first reaction” to a basic language construct is to think it should be replaced with a strategy pattern, then you’ve let coworkers’ bad habits shape your entire programming style.  Expose yourself to some better code, and get better coworkers.  Your “solution” is quite possibly the dumbest programming advice I’ve ever seen.

    Reply
  6. TomerNovember 23, 2009 ב 0:42

    Tzachi,isn’t it “Replace Conditional Dispatcher with Command”?

    Vince,try to replace the

    ‘//do something’ remark with 20-30 lines of code for a ‘real’ algorithm And than see if you changed your mind.

    I always use functions inside ‘case’ statements.

    Reply
  7. Gil FinkNovember 23, 2009 ב 8:05

    @Jeff Darcy,
    Did you read all the post or you just wanted to write a “witty” response and show it to your coworkers?
    First a quete from the post: “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.”.
    Every solution has its place and so is this solution. I only showed one way to apply Strategy Pattern and there are many other solutions out there in the internet.
    To write “Your “solution” is quite possibly the dumbest programming advice I’ve ever seen” as a reaction is only making you sound like someone who jumps to conclusions without reading or understanding what he read.

    Reply
  8. BGNovember 23, 2009 ב 8:21

    I would hate to be the next guy to have to read your code. your approach is quite modular and extendable, but it is not very eye friendly.

    I guess that if you have a lot (15+) of related “strategies” then maybe this would have some merit.

    Reply
  9. jituNovember 23, 2009 ב 9:38

    won’t state design pattern be more effective for this example ?

    Reply
  10. ChrisNovember 23, 2009 ב 23:54

    Your code fails to compile. In your context you’re calling a static method without first initializing the Dictionary object that you’re trying to access.

    It’s a lot of code for something that is handled by a simple construct. But I can see it’s advantages for expansion in the long run.

    Reply
  11. ConkerNovember 24, 2009 ב 0:31

    Jeff was kinda harsh, but i completely agree with him. Bottom line is that you actually increase amount of code, increase complexity, maintenance and reduce readability . I can see use of this pattern being an advantage in some rare specific cases, but simple switch statement like in your example is clearly not one of them.

    Btw, how do you handle “default” case with your dictionary ? How do you handle multiple switch statements ?

    Reply
  12. Gil FinkNovember 24, 2009 ב 8:17

    @Chris,
    The code do compile (I never publish things that I didn’t check) but I agree with the initialization of the Dictionary. I changed the constructor to be static. Pay attention that it is only an example. You can take it to other directions or change it as you like.
    @Conker,
    The example very simple as you wrote. It is only meant to show a concept. As I wrote in the post there are advantages in using this method but I don’t suggest to use it all over the place. In a simple switch which isn’t going to grow or change this solution is a very bad idea. There are cases that I see once in a while that the switch statement that is being used has so many cases with a lot of things to do inside of them or case statements are added once in a while. In such places it is a “code smell” and I prefer to direct the developers to use the Strategy pattern.

    Reply
  13. AdnanNovember 24, 2009 ב 17:45

    Great explanation. Thank you and good job.

    I was reading some of the comments, and it is strange how developers resist moving away from switch statements. They would rather have a linear switch statement than logic branched out into different classes and interface because it is “easier to follow”. That is INSANE and sign of a lazy developer who does not want to take the time to design anything.

    It’s just like people who never wanted to get off the GOTO statement bandwagon and didn’t want functional programming. Now the functional developers don’t want to get into the OO paradigm, no matter what one says.

    Reply
  14. rajeshDecember 4, 2009 ב 12:42

    This is good explanation of using strategy pattern in place of switch case statements. It’s also true that this should be used when switch case statements are much more and are likely to grow in future(to large one).

    Gil, still one question is there (which Conker asked) that “How to handle default case in this approach?”

    Reply
  15. Real lifeJanuary 22, 2010 ב 9:46

    Your code is 10 times longer and 10 times harder to understand and 10 times slower. It’s the same as to say apples is more edible than pears. But I love pears and hate apples. It’s all subjective. You personaly like this way. I hate this way. I tried both. And can tell that you are young. When you get more experienced you will understand that old technlogies were best. It’s young ant not experienced people who like things like this

    Reply