SOLID

Its open but it’s also closed. The Open/ Closed principle!

“A class should be open for Extension but Closed for modification.” This means you should be able to extend the class with additional functionality but the class itself should need no further modifications with the exception of bug fixing.

So below we've got an example, in it we have an age calculator and a customer class, from the customer we can calculate their age:

public class AgeCalculator
{
    public int GetAge(DateTime dob)
    {
        //Calculate and return the age 
    }
}
 
public class Customer
{
    private DateTime _dob { get; set; }
    private AgeCalculator _ageCalculator = new AgeCalculator();
     public Customer(DateTime dob)
    {
        _dob = dob;
    }
     public int GetAge()
    {
        return _ageCalculator.GetAge(_dob);
    }
}

The first thing you should note is that we’re obeying the Single Responsibility Principle by having the age calculation is a separate class, yay!

There are of course other ways to calculate age, like East Asian, or perhaps if you wanted (for some reason) to know the age in Martian years. I appreciate this is a little abstract, but you see my point right? So next we’ll create an interface for the age calculator, which will allow us to use any class that calculates age:

public interface IAgeCalculator
{
    int GetAge(DateTime dob);
}

Then we need to use the interface in our Customer and AgeCalculator classes:

public class AgeCalculator : IAgeCalculator

and :

public class Customer
{
    private DateTime _dob { get; set; }
    private IAgeCalculator _ageCalculator = new AgeCalculator();
}

So we’re currently still using the AgeCalculator class in the Customer class, which is what we’re trying to get away from. There are lots of ways to fix this, some of which we’ll visit in the ‘D’ of SOLID. For now we’ll just pass it to the constructor:

    public Customer(IAgeCalculator ageCalculator, DateTime dob)
    {
        _dob = dob;
        _ageCalculator = ageCalculator;
    }

So we’ve now got a Customer that we can create an instance of with any IAgeCalculator we like. We could have a MartianAgeCalculator or an EastAsianAgeCalculator and as long as they implement the IAgeCalculator interface we can pass them into the Customer constructor and that will be used for age calculations.

Now, we have a class that is open to extension without having to change the class itself i.e. we can alter the way it calculates an age without changing any of the code it uses.

Why is this good? It means that if we want to change the way the Customer calculates age we’re not going to need to change the Customer class at all and if we don’t change it then we can’t break it, great!

Even better, this is hugely useful when we come to writing tests for our code, double win!


Got a comment or correction (I’m not perfect) for this post? Please leave a comment below.
You've successfully subscribed to Gavin Johnson-Lynn!




My Pluralsight Courses: (Get a free Pluaralsight trial)

API Security with the OWASP API Security Top 10

OWASP Top 10: What's New

OWASP Top 10: API Security Playbook

Secure Coding with OWASP in ASP.Net Core 6

Secure Coding: Broken Access Control

Python Secure Coding Playbook