Software Development

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.

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.

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.

References: Refactoring Switches to Classes from our NCG partner John Sonmez at the Making the Complex Simple blog.

Related Articles

Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button