1

Strategy in the Real World

by Jeremy 9. June 2011 16:13

I was recently working with a switch statement that was growing out of control, even as I was developing it.  In my opinion, if you have to scroll to see all cases of a switch statement, it's time to refactor.  So, I used this as an opportunity to apply the strategy pattern.

The switch statement looked like this when I started getting spaghetti-code nausea.  

switch (comparisonOperator)
{
   case Operator.IsEqualTo:
      return WhereFilter.Equal(property, whereCriteria.Value);
   case Operator.Contains:
      string likeExpression = string.Format("%{0}%", whereCriteria.Value);
      return WhereFilter.Like(property, likeExpression);
   case Operator.StartsWith:
      likeExpression = string.Format("{0}%", whereCriteria.Value);
      return WhereFilter.Like(property, likeExpression);
   case Operator.EndsWith:
      likeExpression = string.Format("%{0}", whereCriteria.Value);
      return WhereFilter.Like(property, likeExpression);
   case Operator.IsGreaterThan:
      switch (whereCriteria.ValueType)
         {
            case whereCriteria.ValueType.DateTime:
               return WhereFilter.GreaterThan(property, DateTime.Parse(whereCriteria.Value));
            default:
               throw new NotImplementedException();
         }
   case Operator.IsLessThan:
      switch (whereCriteria.ValueType)
      {
         case whereCriteria.ValueType.DateTime:
            return WhereFilter.LessThan(property, DateTime.Parse(whereCriteria.Value));
         default:
            throw new NotImplementedException();
      }
   //TODO: Implement all cases
   case Operator.IsGreaterThanOrEqualTo:
   case Operator.IsLessThanOrEqualTo:
   case Operator.IsNotEqualTo:
   case Operator.IsNotNull:
   case Operator.IsNull:
   default:
      throw new NotImplementedException();
}


As you can see, it's getting a bit out of hand and there are still quite a few cases (and even nested cases) that still need to be coded.  After I finished applying the pattern, the code in the corresponding class looked like this.

IOperatorStrategy operatorStrategy = OperatorStrategyFactory.GetStrategy(theOperator);
return operatorStrategy.GetWhere(property, criteria);


The OperatorStrategyFactory retrieves the appropriate strategy class based on the input parameter. 

internal static class OperatorStrategyFactory
{
   private static readonly IDictionary<OP, IOperatorStrategy> Strategies = new Dictionary<Operator, IOperatorStrategy>();

   static OperatorStrategyFactory()
   {
      Strategies.Add(Operator.IsEqualTo, new EqualToStrategy());
      Strategies.Add(Operator.Contains, new ContainsStrategy());             //These could be loaded via reflection as well, if beneficial
   }

   public static IOperatorStrategy GetStrategy(Operator @operator)
   {
      if (!Strategies.ContainsKey(@operator))
      {
         throw new NotImplementedException(string.Format("An operator strategy is not registered with the StrategyFactory for the operator '{0}'", @operator));
      }
      return Strategies[@operator];
   }
}


The returned strategy class knows the algorithm required to then supply the necessary result (i.e. where clause in this case). 

internal class EqualToStrategy : IOperatorStrategy
{
   public IWhere GetWhere(IQueryProperty property, Critera criteria)
   {
      //TODO: Implement Algorithm
      throw new NotImplementedException();
   }
}


This is a simple way to make your large switch statements more maintainable and keep the spaghetti-code sometimes associated with them to a minimum.

Currently rated 1.7 by 34 people

  • Currently 1.705882/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

.NET | Abstraction | Design Patterns | Software Maintainability

Powered by BlogEngine.NET 1.4.5.0
Original Design by Laptop Geek, Adapted by onesoft