Home » Avoid… » Switch statements

Switch statements

Content:

Overview

Many developers write switch cases often, like I used to. Switch statements are the very common example of conditional logic. They are the same as if-elseif-elseif…else chains which may grow in length. Learning to avoid switch statements in favour of polymorphism and dictionaries was for me probably the easiest and fastest way to improve coding skills, so I would like to start with this one. After adopting the habit of avoiding switch cases where possible and after some exercise in writing code without them, the process of writing code will be already a different feeling. The outcome will be a cleaner code which communicates the intentions clearer, is less error-prone, and is more object-oriented overall. Writing code with less switch statements improves not only the way the code is written, but also the design of the whole domain model.

Alternatives

  • Polymorphism, moving towards proper object-orientation
  • Dictionary

Example of a switch statement

Following code is an example from the “Refactoring” book by Martin Fowler (“Replace Conditional with Polymorphism” section, page 257). In this article I will take this one as a base, add context to it and then add another feature to the code with switch statement and to the refactored code, comparing the results.

class Employee...
    int payAmount() {
        switch (getType()) {
            case EmployeeType.ENGINEER:
               return _monthlySalary;
            case EmployeeType.SALESMAN:
               return _monthlySalary + _commission;
            case EmployeeType.MANAGER:
               return _monthlySalary + _bonus;
            default:
               throw new RuntimeException("Incorrect Employee");
        }
    }


All my examples below are in C#.

Why to avoid switch statements

  • Every time when another scenario will be added, for example a new enum member, you will need to go through all the places this enum is used, and most probably add another case for a new member. Otherwise there will be a bug if for the new enum member not the desired case but the default case will be used (which can easily throw an exception). So, you have to find, understand and extend every single switch. This will make all the statements longer and increase the amount of conditional logic in you code.
    There will be no compiler error if you add a new member to an enum without modifying the switch statements which evaluates its value. Therfore bugs can be easily planted and overseen. Even if you are aware of all switches to take care of, any other colleague (for example, when you are on holidays) can extend the enum silently and forget some switches.
  • Any time another place may appear, where you will have to do the same switch statement in its entirety but packed with different logic. This will make the problem only larger and increase number of switches, which all must be maintained as described above.
  • Switch statements often indicate a lack of object-orientation, telling that an object is missing for each case. The behaviour which belongs to these multiple objects is exposed through the switch statements in different places in code. Unless you work with not object-oriented language, the objects should be responsible for encapsulating logic based on their data, not the routines. Basically, the switch statements are the legacy from the pre-object-oriented programming era. Polymorphism is the first choice to replace switch statements, but not the only alternative.
  • Even if you have a switch statement evaluating a value/flag/enum just in 1 place in the code, the same can usually be done differently with fewer lines of code, for example with Dictionaries in C#.
  • Often the purpose of the whole switch statement is to assign just one variable to different values depending on id, flag or enum. In order to figure out, whether just one variable gets assigned or something else is happening, you need to go through all cases and check what they are doing. Because every case can assign or not assign the variable. Every case can additionally also do or not do something else. Example: type gets assigned in cases 1 and 2, but not 42. Cases 2 and 42 are also doing something else.

                    case 1:
                        type = EmployeeType.ENGINEER;
                        break;
                    case 2:
                        type = EmployeeType.SALESMAN;
                        DoSomethingElse();
                        break;
                    case 42:
                        DoSomethingElse();
                        break;
    

  • Having a central place with different logic for different types makes it unavoidable to modify this place if the logic is changing even just for one case of the switch statement. Let’s say, in the first example managers should also get a commission:

                case EmployeeType.MANAGER:
                   return _monthlySalary + _commission + _bonus;
    


    Since the whole salary calculating logic for all kinds of employees is inside one method, modifying it may cause side-effects. For example, if in the logic that you changed a value of a field was modified which is used somewhere else. This makes the code more error-prone. Also, in the version history there will be a record that the central class with conditional logic was changed, and if somewhere in this class there is a bug, the small change can’t be excluded from the list of suspects causing it.

Coding

The Employee class

An example from “Refactoring” book is a class Employee, of which we see only the code of the method payAmount(). That’s how the whole class could look like in C#:

    public class Employee
    {
        private int _monthlySalary;
        private int _commission;
        private int _bonus;
        private EmployeeType _type;

        public Employee(int monthlySalary, int commission, int bonus, EmployeeType type)
        {
            _monthlySalary = monthlySalary;
            _commission = commission;
            _bonus = bonus;
            _type = type;
        }

        public int PayAmount()
        {
            switch (_type)
            {
                case EmployeeType.ENGINEER:
                    return _monthlySalary;
                case EmployeeType.SALESMAN:
                    return _monthlySalary + _commission;
                case EmployeeType.MANAGER:
                    return _monthlySalary + _bonus;
                default:
                    throw new Exception("Incorrect Employee");
            }
        }
    }

Data layer: dto and EmployeeFactory

A common scenario is having a database, where employee data is stored, a data layer to fetch this data, and a factory to build the Employee object from dto (data transfer object). Dto is often automatically generated and matches exactly the table row in the database. That’s how dto and factory could look like:

    public class EmployeeSalaryDto
    {
        public string Id { get; set; }
        public int MonthlySalary { get; set; }
        public int Commission { get; set; }
        public int Bonus { get; set; }
        public int TypeId { get; set; }
    }

    public class EmployeeFactory
    {
        public Employee CreateEmployee(EmployeeSalaryDto dto)
        {
            EmployeeType type;
            switch (dto.TypeId)
            {
                case 1:
                    type = EmployeeType.ENGINEER;
                    break;
                case 2:
                    type = EmployeeType.SALESMAN;
                    break;
                case 42:
                    type = EmployeeType.MANAGER;
                    break;
                default:
                    throw new Exception("Incorrect Employee Type Id: " + dto.TypeId);
            }
            return new Employee(dto.MonthlySalary, dto.Commission, dto.Commission, type);
        }
    }

I planted another switch to the factory. This is quite a common case when the purpose of the whole switch statement is the assignment of variable ‘type’ for a number of supported scenarios (values of dto.TypeId).
If occurred scenario is not supported (for example, TypeId=3), an exception is thrown which is done in a “default case” of switch statement. That’s another paradox – ‘default’ is not a default case but an exceptional error case.
The logic of this switch statement consists of 4 cases:

  • 1: if typeId is 1 – do something
  • 2: if it’s not 1 but 2 – do something else
  • 3: if it’s not 1 or 2 but 42 – do something different
  • 4: if it’s neither 1, 2 or 42 – report an error.

The ‘type’ variable is assigned in 3 places in this code. I regularly find switches having many more cases in every code base. But I’ll keep this example shorter and closer to Martin Fowler’s example.

Replacing with Dictionary

Here is an example with exactly the same logic implemented with fewer lines of code using a Dictionary, which is declared as a private field:

Switch statement:

            ...
            switch (dto.TypeId)
            {
                case 1:
                    type = EmployeeType.ENGINEER;
                    break;
                case 2:
                    type = EmployeeType.SALESMAN;
                    break;
                case 42:
                    type = EmployeeType.MANAGER;
                    break;
                default:
                    throw new Exception("Incorrect Employee Type Id: " + dto.TypeId);
            }
            ...
  Dictionary:

        ...
        if (!_typeMap.ContainsKey(dto.TypeId))
            throw new Exception("Incorrect Employee Type Id: " + dto.TypeId);
        type = _typeMap[dto.TypeId];
        ...
        private readonly Dictionary<int, EmployeeType> _typeMap
			= new Dictionary<int, EmployeeType>
        {
            {1, EmployeeType.ENGINEER},
            {2, EmployeeType.SALESMAN},
            {42, EmployeeType.MANAGER}
        };

Using Dictionary instead of switch statement makes the goal clear – it’s the assignment of ‘type’. The assignment is happening now only in 1 place instead of 3. The logic is reduced to 2 cases:

  • if it’s known what to do for typeId – do it
  • if it’s not known what to do for typeId – report an error.

Checking if it’s 1, 2 or 42 is all done by Dictionary, all you have to do is to initialize it.

The same could be done with the switch in Employee class:

Switch statement:

        public int PayAmount()
        {
            switch (_type)
            {
                case EmployeeType.ENGINEER:
                    return _monthlySalary;
                case EmployeeType.SALESMAN:
                    return _monthlySalary + _commission;
                case EmployeeType.MANAGER:
                    return _monthlySalary + _bonus;
                default:
                    throw new Exception("Incorrect Employee");
            }
        }
  Dictionary:

        public int PayAmount()
        {
            var salaryCalculation = new Dictionary<EmployeeType, Func<int>>
            {
                {EmployeeType.ENGINEER, () => _monthlySalary},
                {EmployeeType.SALESMAN, () => _monthlySalary + _commission},
                {EmployeeType.MANAGER, () => _monthlySalary + _bonus}
            };

            if (salaryCalculation.ContainsKey(_type))
                return salaryCalculation[_type].Invoke();
            throw new Exception("Incorrect Employee");
        }

For a factory in a data layer of an application it worked fine – it has to somehow interpret ids from the database and resolve them to employee types in code. But for salary calculation it did not work that well. There is still one method containing different logic based on employee type. This can be done better with polymorphism.

Replacing with polymorphism using an abstracts base class

That’s the result of the refactoring by replacing switch statement with polymorphism, like it’s done in the “Refactoring” book:

    public abstract class EmployeeType
    {
        public abstract int PayAmount(Employee emp);
    }

    public class Engineer : EmployeeType
    {
        public override int PayAmount(Employee emp)
        {
            return emp.MonthlySalary;
        }
    }

    public class Salesman : EmployeeType
    {
        public override int PayAmount(Employee emp)
        {
            return emp.MonthlySalary + emp.Commission;
        }
    }

    public class Manager : EmployeeType
    {
        public override int PayAmount(Employee emp)
        {
            return emp.MonthlySalary + emp.Bonus;
        }
    }


The result is a number of very small classes, and many developers seem to have a problem with creating small classes for everything. My main motivation in this article is to show that creating small classes is nothing but laying a foundation for object-oriented model. I will show in next steps, how these small classes will grow with features, resulting in a number of classes each of which can be changed completely separately from others.

Replacing with polymorphism using an interface

First I would like to model these initial small objects differently. I don’t want PayAmount method to take any arguments. I want each of 3 Employee types to contain all data which is relevant for them. As a consequence, irrelevant data won’t be a part of the class. Example – bonus and commission are irrelevant for Engineer, so the Engineer class won’t have fields for them, which makes the class cleaner and smaller, nicely reducing it’s content to what it should be. I will also use an interface instead of abstract class.


    public interface IEmployee
    {
        int PayAmount();
    }

    public class Engineer : IEmployee
    {
        private int _monthlySalary;

        public Engineer(int monthlySalary)
        {
            _monthlySalary = monthlySalary;
        }

        public int PayAmount()
        {
            return _monthlySalary;
        }
    }

    public class Salesman : IEmployee
    {
        private int _monthlySalary;
        private int _commission;

        public Salesman(int monthlySalary, int commission)
        {
            _monthlySalary = monthlySalary;
            _commission = commission;
        }

        public int PayAmount()
        {
            return _monthlySalary + _commission;
        }
    }

    public class Manager : IEmployee
    {
        private int _monthlySalary;
        private int _bonus;

        public Manager(int monthlySalary, int bonus)
        {
            _monthlySalary = monthlySalary;
            _bonus = bonus;
        }

        public int PayAmount()
        {
            return _monthlySalary + _bonus;
        }
    }

How polymorphism works

Polymorphism is one of the main concepts in object-oriented development. Polymorphism occurs when a number of different classes with different implementations of the same methods can be used not strongly typed but through the interface or their base class without a need to differentiate between them.

Example: you must calculate a sum of all salaries of all employees. If you have an array of IEmployee, it may contain Engineers, Salesmen and Managers mixed inside, but you don’t know (and actually you don’t even care) who is who. Because all you care about is the salary, which is returned by PayAmount method by each one of them. That’s the only thing you can do with them as long as their common interface has just this method.

        private IEmployee[] _employees;

        public int GetSumOfAllSalaries()
        {
            return _employees.Sum(x => x.PayAmount());
        }

Adding a new Employee type: Reseller

One may consider to use a base class for all 3 Employee types with common fields, like monthlySalary. But look what happens if a new Employee type will be added who has no fixed monthly salary – a reseller!

Having IEmployee interface (or abstract class with abstract methods), already implemented by all other employee types, makes it obvious what do you need for another employee type. All interface methods are obligatory for classes implementing the interface. There will be a compiler error if Reseller implements IEmployee but does not have a public PayAmount() method from its interface.

    public class Reseller : IEmployee
    {
        private int _commission;
        private readonly int _bonus;

        public Reseller(int commission, int bonus)
        {
            _commission = commission;
            _bonus = bonus;
        }

        public int PayAmount()
        {
            return _commission + _bonus;
        }
    }

Now the only place where you need to take care of the new Reseller type is the factory where it is created. The factory now creates different objects which all implement IEmployee interface. To add a reseller only 1 dictionary entry is required with its ID as a key. This is another benefit comparing to switch-case. No logic has to be modified, no additional statements and conditions have to be added.

    public class EmployeeFactory
    {
        public IEmployee CreateEmployee(EmployeeSalaryDto dto)
        {
            if (_typeMap.ContainsKey(dto.TypeId))
                return _typeMap[dto.TypeId].Invoke(dto);
            throw new Exception("Incorrect Employee Type Id: " + dto.TypeId);
        }

        private readonly Dictionary<int, Func<EmployeeSalaryDto, IEmployee>> _typeMap
                         = new Dictionary<int, Func<EmployeeSalaryDto, IEmployee>>
        {
            {1, dto => new Engineer(dto.MonthlySalary)},
            {2, dto => new Salesman(dto.MonthlySalary, dto.Commission)},
            {42, dto => new Manager(dto.MonthlySalary, dto.Bonus)},
            {43, dto => new Reseller(dto.Commission, dto.Bonus)}
        };
    }

New feature: travel expenses calculation

Now a new feature request comes in. The calculation of travel expenses must be implemented differently for different employee types.

Switch statement example:   Polymorphism:
    public class TravelExpenseCalculator
    {
        public decimal Calculate(EmployeeType employeeType, int kilometersTravelled)
        {
            switch (employeeType)
            {
                case EmployeeType.ENGINEER:
                    return kilometersTravelled * 0.2m;
                case EmployeeType.SALESMAN:
                {
                    if (kilometersTravelled < 30)
                        return 0;
                    return (kilometersTravelled - 30) * 0.25m;
                }
                case EmployeeType.MANAGER:
                    return kilometersTravelled * 0.3m;
                    //Forgot reseller?
                case EmployeeType.RESELLER:
                    return kilometersTravelled * 0.25m;
                default:
                    throw new Exception("Incorrect Employee");
            }
        }
    }
 
    public interface IEmployee
    {
        int PayAmount();
        decimal CalculateTravelExpense(int kilometersTravelled);
    }

    public class Engineer : IEmployee...
        public decimal CalculateTravelExpense(int kilometersTravelled)
        {
            return kilometersTravelled * 0.2m;
        }

    public class Salesman : IEmployee...
        public decimal CalculateTravelExpense(int kilometersTravelled)
        {
            if (kilometersTravelled < 30)
                return 0;
            return (kilometersTravelled - 30) * 0.25m;
        }

    public class Manager : IEmployee...
        public decimal CalculateTravelExpense(int kilometersTravelled)
        {
            return kilometersTravelled * 0.3m;
        }

    public class Reseller : IEmployee...
    {
        public decimal CalculateTravelExpense(int kilometersTravelled)
        {
            return kilometersTravelled * 0.25m;
        }
    }

This happens often in reality. Here the enum spreads over the code resulting in another places where it’s required to go through all types and apply conditional logic based on value.
 
Once the method CalculateTravelExpense() is added to an interface, all 4 classes will not compile until every of them implements this method. No conditional logic, no exposure of implementation details to “TravelExpenseCalculator”. There is even no need for a “TravelExpenseCalculator”, every Employee type is able to calculate expenses by itself.

If you want to see how many lines of code came out at the end for the business logic of both solutions, I analysed it on a separate page here.

One step further towards object-orientation

The examples above show how polymorphism and dictionary do the job of switch statements providing a number of benefits. But if you look closely, the code can become more object-oriented. For example, if integers or decimals stand for money amounts, why don’t create a class for that? This class can validate itself, for example checking in the constructor (which accepts decimal or integer) that given amount is not negative. In C# you can easily implement any operators on them which you need, like + to add two amounts of money, * to multiply amount of money with a number, to compare two amounts. The same with Kilometers. In this case the code becomes much clearer. For example, commission can be a percentage or a fixed money amount. Actually I had to work with the code where the same class member was used sometimes for a fixed amount and sometimes for a percentage, which was confusing and error-prone. Here is a refactored code of Reseller class where there can be no confusion about how exactly to interpret integers or decimals, because these got replaced with smaller classes, names of which clearly communicate what’s inside. I also cleaned this class up a bit.

    public class Reseller : IEmployee
    {
        private MoneyAmount _commission;
        private readonly MoneyAmount _bonus;

        public Reseller(MoneyAmount commission, MoneyAmount bonus)
        {
            _commission = commission;
            _bonus = bonus;
        }

        public MoneyAmount PayAmount()
        {
            return _commission + _bonus;
        }

        public MoneyAmount CalculateTravelExpense(Kilometers kilometersTravelled)
        {
            var costPerKilometer = new MoneyAmount(0.25m);
            return kilometersTravelled * costPerKilometer;
        }
    }

Further reading

That’s what the books, which I refer to say about switch statements:

  • Martin Fowler’s “Refactoring” calls a comparative lack of switch statements “one of the most obvious symptoms of object-oriented code”. Switch statements are listed there as one of the “Bad Smells in Code”. “Replace Conditional with Polymorphism” is a suggested refactoring method (Chapter 9 “Simplifying Conditional Expressions”).
  • Clean Code” by Robert C. Martin deals with switch statements in a similar way in Chapters 3.4 and G23. The key rule is to allow only one switch statement evaluating a type and to put it in the lowest level of application code – for example, to the factory which creates polymorphic objects.
    Switch statements also violate the principles of object-oriented programming. These are:

    • Single Responsibility Principle (wikipedia), because they do several different things depending on switch value.
    • Open-Closed Principle (wikipedia), because they must be changed every time a new type is added.

Level Up Code, November 2013


2 Comments

  1. Artem says:

    I would not say replacing “switch” with “dictionary” makes it much clearer and helps to avoid repeating of logic across the code and also it produces much inefficient code in terms of performance (which of course is not considered when making code clearer)
    Polymorphism is the only way that makes it actually clearer and more generic. However in some cases it is easier to keep on with switch. My rule of thumb is usually: if I need to introduce “switch” with same conditions second time, but different logic – it is a good sign to introduce something
    that prevents repeating and usually this is introducing some abstraction with different implementation…

    • levelupcode says:

      Thanks for reply, Artem (and sorry for late response).

      How would you implement an EmployeeFactory in the data layer?

      I feel that dictionary makes the code cleaner by reducing the logic comparing to switch-statement. But polymorphism is, surely, a preferable way to go.

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

%d bloggers like this: