Tim Weaver

A .NET Blog

<November 2008>
SuMoTuWeThFrSa
2627282930311
2345678
9101112131415
16171819202122
23242526272829
30123456


Navigation

Work

Subscriptions

Post Categories



Wednesday, May 04, 2005 - Posts

Strategy Pattern

How many times have you read something along the lines of "replace switch statement with strategy pattern"?

I've read it a lot and to be honest I never really "got it". I've read a number of patterns books and looked at numerous examples but they always left me wondering how that gets rid of the switch. After all, I thought, you still have to make a determination on what gets called...

It's all about the Pattern
I decided to end my ignorance. It turns out that the problem was with how I was interpreting the pattern. I kept thinking that you would still have to use a switch/if then else or something to figure out what to call, but that simply isn't true. Using the Strategy pattern you move that decision step back up the call stack [I'll probably get smacked around for that statement, but it fits my mental picture].

How about an Example
In order to have this make sense I created a non trivial example. In it we have a class RequestHandlerOriginal that has a single method ProcessUserRequest that takes in an enum value and a userID. Based upon the enum it will call out to the appropriate method. In the below example I left the calls to the Worker.* methods. There isn't really any good reason for this class to exist. In reality these methods could/should be folded into the strategy classes. This cuts down on the actual amount of code you have to maintain. Here's the original: [this is code I just tossed together. It compiles, but that is my only promise]

    5     /// <summary>

    6     /// The original implementation

    7     /// </summary>

    8     public class RequestHandlerOriginal

    9     {

   10         public enum ActionType

   11         {

   12             Login,

   13             LogOut,

   14             ChangePassword,

   15             ResetPassword

   16         }

   17         #region Constructor

   18         public RequestHandlerOriginal(){}

   19         #endregion

   20         #region ProcessUserRequest

   21         /// <summary>

   22         /// Takes in the ActionType and the userID and then based upon the requested action it will

   23         /// call the appropriate method

   24         /// </summary>

   25         /// <param name=3D"action"></param>

   26         /// <param name=3D"userID"></param>

   27         public void ProcessUserRequest(ActionType action, int userID)

   28         {

   29             switch(action)

   30             {

   31                 case ActionType.Login:

   32                     Workers.ProcessLoginRequest(userID);

   33                     break;

   34                 case ActionType.LogOut:

   35                     Workers.ProcessLogOutRequest(userID);

   36                     break;

   37                 case ActionType.ChangePassword:

   38                     Workers.ProcessChangePasswordRequest(userID);

   39                     break;

   40                 case ActionType.ResetPassword:

   41                     Workers.ProcessResetPasswordRequest(userID);

   42                     break;

   43                 default:

   44                     throw new ArgumentOutOfRangeException("ActionType", action, "Unable to handle the requested action");

   45             }

   46         }

   47         #endregion

   48  

   49     }

Now here is how it is called

   96         public void CallOriginal()

   97         {

   98             RequestHandlerOriginal orig =3D new RequestHandlerOriginal();

   99             orig.ProcessUserRequest(RequestHandlerOriginal.ActionType.ChangePassword, 11111);

  100         }

Here are the new class(s) based upon the Strategy Pattern

   50     #region Strategy Base Class

   51     /// <summary>

   52     /// This is the base class that all strategy participants inherit from

   53     /// </summary>

   54     public abstract class RequestHandlerNew

   55     {

   56         public abstract void ProcessUserRequest(int userID);

   57     }

   58     #endregion

   59     #region Concrete Strategy classes

   60     public class RequestHandlerLogin:RequestHandlerNew

   61     {

   62         public override void ProcessUserRequest(int userID)

   63         {

   64             Workers.ProcessLoginRequest(userID);

   65         }

   66     }

   67     public class RequestHandlerLogOut:RequestHandlerNew

   68     {

   69         public override void  ProcessUserRequest(int userID)

   70         {

   71             Workers.ProcessLogOutRequest(userID);

   72         }

   73     }

   74     public class RequestHandlerChangePassword:RequestHandlerNew

   75     {

   76         public override void ProcessUserRequest(int userID)

   77         {

   78             Workers.ProcessChangePasswordRequest(userID);

   79         }

   80     }

   81     public class RequestHandlerResetPassword:RequestHandlerNew

   82     {

   83         public override void ProcessUserRequest(int userID)

   84         {

   85             Workers.ProcessResetPasswordRequest(userID);

   86         }

   87     }

   88     #endregion

Here is how to call the new class(s)

   64         public void CallStrategy()

   65         {

   66             RequestHandlerNew newHandler =3D new RequestHandlerChangePassword();

   67             newHandler.ProcessUserRequest(11111);

   68         }

Here is the worker class (just so the code compiles as promised)

   74     public class Workers

   75     {

   76         protected Workers(){}

   77         #region worker methods

   78         public static void ProcessLoginRequest(int userID){ /*do something*/  }

   79         public static void ProcessLogOutRequest(int userID){ /*do something*/  }

   80         public static void ProcessChangePasswordRequest(int userID){ /*do something*/  }

   81         public static void ProcessResetPasswordRequest(int userID){ /*do something*/  }

   82         #endregion

   83     }

Conclusion
At the point of the CallOriginal() method call the developer had to make a decision about what action was being taken. The reason I say that is because the enum ActionType had to be set. Likewise with the Strategy pattern the decision about what is being called is made at the time of the call the difference is that it is explicit instead of implicit. The switch statement is no longer needed because we don't have the additional intermediate method.

posted Wednesday, May 04, 2005 3:47 AM by icodemarine




Powered by Dot Net Junkies, by Telligent Systems