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.