Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.

What is SOLID?

This is a series on the basics of the SOLID principles of software engineering. The SOLID principles were created by Robert C. Martin “Uncle Bob” who is a software engineer public speaker and author.

SOLID is an acrostic that stands for:

There will be an article for each. Lets take a look at open close.

Open Close

Non Compliant Example

class FullTimeEmployee
{
    public double Salary = 50000;
    public int PayPeriodsPerYear = 24;

}

class ContractEmployee
{
    public double PayPerHour = 30;

    public int NumberOfHoursWorked = 40;
}

class CompensationCalculator
{
    public double CalculatePayPerPayPeriod(object employee)
    {
        if (employee is FullTimeEmployee fte)
        {
            return fte.Salary / fte.PayPeriodsPerYear;
        }
        else if (employee is ContractEmployee ce)
        {
            return ce.NumberOfHoursWorked * ce.PayPerHour;
        }
        else
        {
            return 0;
        }
    }
}

In this case we have two classes that represent two different employee types who’s compensation is calculated differently. There is a third class where the compensation logic lives. But in order to make this calculation this method needs to check what type of employee we have then make the calculation.

This is bad in a number of ways.

  • What happens if someone passes a non employee type to method?

    Nothing at compile time would tell you there was something wrong.

  • What if you add another employee type, like part time employees?

    You have to know to go out to all your methods like this one and add more branching logic to deal with these additional calculations. Think about real world examples that could require you to update logic in hundreds of places. You also could potentially introduce bugs into your already working and tested code. This use case means that this code is not closed for modification.

Compliant Example

public abstract class Employee
{
    public abstract double CalculatePayPerPayPeriod();

}

class FullTimeEmployee : Employee
{
    private double Salary = 50000;
    private int PayPeriodsPerYear = 24;

    public override double CalculatePayPerPayPeriod()
    {
        return Salary / PayPeriodsPerYear;
    }
}

class ContractEmployee : Employee
{
    private double PayPerHour = 30;

    private int NumberOfHoursWorked = 40;
    public override double CalculatePayPerPayPeriod()
    {
        return NumberOfHoursWorked * PayPerHour;
    }
}

In this example there is an abstract employee class which each of the employee types implement. So now if I add a new employee type I can do it like this:

public abstract class Employee
{
    public abstract double CalculatePayPerPayPeriod();

}

class FullTimeEmployee : Employee
{
    private double Salary = 50000;
    private int PayPeriodsPerYear = 24;

    public override double CalculatePayPerPayPeriod()
    {
        return Salary / PayPeriodsPerYear;
    }
}

class ContractEmployee : Employee
{
    private double PayPerHour = 30;

    private int NumberOfHoursWorked = 40;
    public override double CalculatePayPerPayPeriod()
    {
        return NumberOfHoursWorked * PayPerHour;
    }
}

class PartTimeEmployee : ContractEmployee
{
    private double CompensationPerPayPeriod = 2500;
    public override double CalculatePayPerPayPeriod()
    {
        return CompensationPerPayPeriod;
    }
}

This way I’ve added a Part Type Employee who shares some aspects of contract employees but needs to calculate compensation differently. I can add the logic where it belongs and I do not have alter the other classes.

Conclusion

In general if you see code that is doing type checking like this there is some code smell, and probably a violation of the open close principal.

Here is a bit of Robert Martin’s recent take on the validity of this principle in 2020:

… we want to make sure that we don’t have to change the right code just to make the wrong code work again

I think that is a very concise way of explaining what this principal is and why it is so important.