I was working with some colleagues the other day and we stumbled across some old code that exhibited the switch statement code smell. It was a particularly concerning one since it existed inside a service-type class (i.e. NotificationService, SomeExternalSystemWeDontCareAboutService, etc). For a really simple example, let's say it was a currency conversion service. There was one piece of information the service needed from the focus object being passed in and then it was supposed to be able to deliver on its service requirements (converting the currency).

The primary method in this service took an implementation of an interface which allowed it to get the amount it was supposed to convert. Now, it should be noted that you probably wouldn't do this -- you'd just take the amount directly rather than an indirection interface. But please ignore that for this example. I want to focus specifically on the offending switch statement.

This class was not supposed to know about the specific implementations of the focus interface implementation being supplied. Over time, however, it evolved to have specific cases in a switch statement to actually get the money amount it was supposed to convert from the focus interface. This was good example, if slightly different in form, of a Leaky Abstraction as described by Jeremy Miller in his recent blog post on the subject.

The code went a little something like this (incriminating names of interfaces and methods changed to make the sample easier to understand):

public decimal ConvertCurrencyFromItem( IFocusItem item)
{
    //... uninteresting code

    decimal amount = 0.0M;

   
switch(  item.GetType().Name )
    {
        case "SomeStateThatHasWeirdConversionLawsImplementation":
            //... These guys have to have some pre-processing before we can convert

            amount = ... // some specialized code

            //... uninteresting code
            break;
        case "SomeGovernmentAgencyWithNoTaxImplementation":
            //... These guys get some weird tax rules applied

            amount = ... // some specialized code

            //... uninteresting code
            break;
        case "SomeCompanyThatGetsASpecialRate":
            //... We have a special deal with these guys, they get below-market rates

            amount = ...
// some specialized code

            //... uninteresting code
            break;
        default:

            amount = item.Amount;

            //... uninteresting code
            break;
    }


    //... uninteresting code that does the conversion using the 'amount' variable

}

This code would certainly be used on a new project they were planning and I recommended it as a good candidate for high priority refactoring going forward. I suggested using the Strategy Design Pattern to remove the specific knowledge of each implementation out of the service, while still letting the service do what it was supposed to do (convert currency).

Now, keep in mind this is a contrived example, so you might have a completely different (and better solution), but I'm just trying to illustrate how/when you might use the strategy pattern.

Let's start with our basic strategy interface

public interface IGetAmountStrategy
{
      decimal GetAmount( IFocusItem item );
}

Next, we get rid of our switch and move the specific logic into separate strategy implementations.

public class SomeStateWithWeirdConversionLawsStrategy : IGetAmountStrategy
public class SomeGovernmentAgencyWithNoTaxStrategy : IGetAmountStrategy
etc, etc, etc

And then you'd probably have the default strategy:

public class DefaultAmountStrategy : IGetAmountStrategy
{
      public decimal GetAmount( IFocusItem item )
      {
            return item.Amount;
      }
}

Our ConvertCurrencyFromItem method now looks like:

public decimal ConvertCurrencyFromItem( IFocusItem item, IGetAmountStrategy amountStrat )
{
    //... uninteresting code

    decimal amount = amountStrat.GetAmount( item );
    
    //... uninteresting code

}

An important benefit to using this refactoring is that ConvertCurrentFromItem is now quite easy to test. We can mock both the IFocusItem and IGetAmountStrategy dependencies and test only what we want to test: The currency conversion. Before, with the switch statement, we had to test all the special amount-getting cases in addition to the actual currency conversion.