Do you REALLY know what SOLID principles means? Think again! (Part 2: O)

Open for Extension, Closed for Modification

Classes should be open for extension, but closed for modification. By doing so, we stop ourselves from modifying existing code and causing potential new bugs. What Open Close Principle wants to say is – We should be able to add new functionality without touching the existing code for the class.

This is because whenever we modify the existing code, we are taking the risk of creating potential bugs. So we should avoid touching the tested and reliable (mostly) production code if possible.
But how are we going to add new functionality without touching the class?

It is usually done with the help of interfaces and abstract classes.
We can make sure that our code is compliant with the open/closed principle by utilising inheritance and/or implementing interfaces that enable classes to polymorphically substitute for each other.

Consider the following example:

class Post
{
    void CreatePost(Database db, string postMessage)
    {
        if (postMessage.StartsWith("#"))
        {
            db.AddAsTag(postMessage);
        }
        else
        {
            db.Add(postMessage);
        }
    }
}

In this example we need to do something specific whenever a post starts with the character ‘#’.
However, the above implementation violates the open/closed principle in the way this code differs the behaviour on the starting letter. If we later wanted to also include mentions starting with ‘@’, we’d have to modify the class with an extra ‘else if’ in the CreatePost() method.

Let’s try to make this code compliant with the open/closed principle by simply using inheritance.

class Post
{
    void CreatePost(Database db, string postMessage)
    {
        db.Add(postMessage);
    }
}

class TagPost : Post
{
    override void CreatePost(Database db, string postMessage)
    {
        db.AddAsTag(postMessage);
    }
}

By using inheritance, it is now much easier to create extended behaviour to the Post object by overriding the CreatePost() method. The evaluation of the first character ‘#’ will now be handled elsewhere of our software, and if we want to change the way a postMessage is evaluated, we can change the code there, without affecting any of these underlying pieces of behaviour.

The general idea of this principle is great.

It tells us to write our code so that we will be able to add new functionality without changing the existing code. That prevents situations in which a change to one of our classes also requires us to adapt all depending classes. Unfortunately, many of the tutorial out there proposes (the example given above is one such case; sadly) to use inheritance to achieve this goal.
But more often than not; inheritance introduces tight coupling if the sub-classes depend on implementation details of their parent class.

That’s why we need to redefine the Open/Closed Principle to the Polymorphic Open/Closed Principle. It uses interfaces instead of super-classes to allow different implementations which you can easily substitute without changing the code that uses them. The interfaces are closed for modifications, and we can provide new implementations to extend the functionality of your software.
The main benefit of this approach is that an interface introduces an additional level of abstraction which enables loose coupling. The implementations of an interface are independent of each other and don’t need to share any code.

But an interesting observation is – the word extend doesn’t necessarily mean that you should subclass the actual class that needs the new behaviour. Let’s consider an example:

public class Context {
    private IBehavior behavior;

    public void doStuff() {
        if (this.behavior != null)
            this.behavior.doStuff();
    }

    public void setBehavior(IBehavior behavior) {
        this.behavior = behavior;
    }
}

public interface IBehavior {
    public void doStuff();
}

..............................

public class HelloWorldBehavior implements IBehavior {
    public void doStuff() {
        System.println("Hello world!");
    }
}

public class GoodByeBehavior implements IBehavior {
    public void doStuff() {
        System.out.println("Good bye cruel world!");
    }
}

...................................

// in main method
Context c = new Context();

c.setBehavior(new HelloWorldBehavior());
c.doStuff();
// prints out "Hello world!"

c.setBehavior(new GoodByeBehavior());
c.doStuff();
// prints out "Good bye cruel world!"

In the example above the Context is locked for further modifications.
Most programmers would probably want to subclass the class in order to extend it but here we don’t because it assumes it’s behaviour can be changed through anything that implements the IBehavior interface. Using this pattern we can modify the behaviour of the context at runtime, through the setBehavior method as extension point.
So whenever we want to extend the “closed” context class, let’s try do it by sub-classing it’s “open” collaborating dependency.

OCP: How to Follow

just like the SRP, the way we follow this principle in practice is determined by an educated guess about how the requirements on our software are likely to change in future. In the SRP we make a judgement about decomposition and where to draw encapsulation boundaries in our code. In the OCP, we make a judgement about what in our module we will make abstract and leave to our module’s consumers to make concrete, and what concrete functionality to provide by us. Any sensible module must lie in between these extremes: to do something useful it has to be partially concrete, however to be used in a range of contexts it must be partially abstract.

Violating one principle but following the other: Follows OCP but not LSP

Lets say we have the given code:

public interface IPerson {}

public class Boss implements IPerson {
    public void doBossStuff() { ... }
}

public class Peon implements IPerson {
    public void doPeonStuff() { ... }
}

public class Context {
    public Collection<IPerson> getPersons() { ... }
}

This piece of code follows the open-closed principle. If we’re calling the context’s GetPersons method, we’ll get a bunch of persons all with their own implementations. That means that IPerson is closed for modification, but open for extension.
However things take a dark turn when we have to use it:

// in some routine that needs to do stuff with 
// a collection of IPerson:
Collection<IPerson> persons = context.getPersons();
for (IPerson person : persons) {
    // now we have to check the type... :-P
    if (person instanceof Boss) {
        ((Boss) person).doBossStuff();
    }
    else if (person instanceof Peon) {
        ((Peon) person).doPeonStuff();
    }
}

We have to do type checking and type conversion which violates LSP! We can get out of this situation by either doing some pull-up refactoring or implementing a Visitor pattern.
In this case we can simply do a pull up refactoring after adding a general method:

public class Boss implements IPerson {
    // we're adding this general method
    public void doStuff() {
        // that does the call instead
        this.doBossStuff();
    }
    public void doBossStuff() { ... }
}


public interface IPerson {
    // pulled up method from Boss
    public void doStuff();
}

// do the same for Peon

The benefit now is that we don’t need to know the exact type anymore, following LSP:

// in some routine that needs to do stuff with 
// a collection of IPerson:
Collection<IPerson> persons = context.getPersons();
for (IPerson person : persons) {
    // yay, no type checking!
    person.doStuff();
}

Open close principle is about locking the working code down but still keeping it open somehow with some kind of extension points. This is to avoid code duplication by encapsulating the code that changes. It also allows for failing fast as breaking changes are painful (i.e. change one place, break it everywhere else).
For the sake of maintenance the concept of encapsulating change is a good thing, because changes always happen.