Advertisements
Posted by: jsonmez | February 21, 2012

Refactoring Switches to Classes

I’ve talked about refactoring switch statements several times before.

I’ve even created a defaultable dictionary for refactoring a switch statement into a dictionary of actions.

This time, I am going to talk about refactoring switches when you have switch statements operating on the same set of data, but have different actions in different circumstances.

switch

First let’s recap

When I talked about refactoring switches before, we were mainly dealing with a single switch statement somewhere in code.

In the case where you have only a single switch statement, or multiple switch statements that do the same thing based on the data, using a dictionary is still a great way to go.

However, there are going to be circumstances where you are going to be switching on the same data, but in different contexts.  In these cases, you will want to perform different actions.

Let’s look at an example.

// In fighting code
switch(classType)
{
    case WARRIOR:
          swingSword();
          break;
    case MAGE:
          castSpell();
          break;
    case THIEF:
          backstab():
          break;
}

// In wear armor code
switch(classType)
{
    case WARRIOR:
          return CAN_WEAR;
    case MAGE:
          return isConsideredLightArmor(armor);
    case THIEF:
          if(isSneaking)
              return NOT_NOW;
          return isConsideredLightArmor(armor);
}

In this example, we are switching on the same enumeration, but we are doing it in different locations of the code.

Using a dictionary would not work well here because we would need multiple dictionaries.

We still don’t want to leave this as it is though, because the code is pretty messy and fragile.

Mage

Separation of concerns

The problem is the code that contains these switch statements has too much responsibility.  It is being asked to handle logic for each one of our character class types.

What we need to do to improve this code is refactor the enumerations into their own classes.  Each switch statement will become a method that will be implemented by our enumeration based class.

If we are using Java, we can use Java’s enumeration implementation that allows for methods on an enumeration.  If we are using a language like C#, we still have to map the enumeration value to each class.

Let’s start by making our classes.

First we need a base class, or interface.

public interface CharacterClass
{
    void Attack();
    ArmorResponse WearArmor(armor);
}

Now we can create classes that implement this interface, that contain the logic that was in each switch statement.

public class Warrior : CharacterClass
{
    void Attack()
    {
       swingSword();
    }

    ArmorResponse WearArmor(armor)
    {
       return CAN_WEAR;
    }
}

public class Mage : CharacterClass
{
    void Attack()
    {
       castSpell();
    }

    ArmorResponse WearArmor(armor)
    {
       return isConsideredLightArmor(armor);
    }
}

public class Thief : CharacterClass
{
    void Attack()
    {
       backstab();
    }

    ArmorResponse WearArmor(armor)
    {
       if(isSneaking)
           return NOT_NOW;
       return isConsideredLightArmor(armor);
    }
}

Next we can map our enumeration to our class.

public Dictionary characterDictionary =
    new Dictionary {
    { WARRIOR, new Warrior() },
    { MAGE, new Mage() },
    { THIEF, new Thief() }
};

We could also get rid of the enumeration if we wanted, and just create the appropriate class.  It will depend on what your existing code looks like.

No more switches!

Now let’s take a look at what we end up with in the two locations where we had switches.

// In fighting code
myCharacter.Attack();

// In wear armor code
var armorResponse =  myCharacter.WearArmor(armor);

If we want to add a new character class type, we just add a new class that implements the CharacterClass interface and put a mapping in our dictionary, or in our character initialization code.

If we end up having other places in our logic where different character class types should have different behavior, we just add a method to our CharacterClass interface and implement it in any classes that implement CharacterClass.

Our code is much more maintainable, and easier to understand.

Advertisements

Responses

  1. […] Refactoring Switches to Classes – John Sonmez looks at another scenario where you may encounter switch statements which break separation of concerns, and looks at how you can refactor them to a more object oriented approach. […]


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Categories

%d bloggers like this: