The Open Closed Principle (OCP) is the SOLID principle which states that the software entities (classes or methods) should be open for extension but closed for modification.

But what does this really mean?

Basically, we should strive to write a code which doesn’t require modification every time a customer changes its request. Providing such a solution where we can extend the behavior of a class (with that additional customer’s request) and not modify that class, should be our goal most of the time.

In this article, we will show you how to write the code by following the Open Closed Principle with two different examples. Initially, none of the examples will obey the OCP rules, but right after the initial development, we are going to refactor the code using the OCP.

To download the source code for this project, check out the Open Closed Principle Project Source Code.

To read about other SOLID principles, check out our SOLID Principles page.

This article is divided into the following sections:

So, let’s jump right into it.

Salary Calculator Example

Let’s imagine that we have a task where we need to calculate the total cost of all the developer salaries in a single company. Of course, we are going to make this example simple and focus on the required topic.

To get started, we are going to create the model class first:

Once we’ve created our model, we can transition to the salary calculation feature:

Now, all we have to do is to provide some data for this class and we are going to have our total costs calculated:

Our result should be:

Open Closed Principle first example

So, all of this is working great, but now our boss comes to our office and says that we need a different calculation for the senior and junior developers. The senior developers should have a bonus of 20% on a salary.

Of course, to satisfy this requirement, we are going to modify our CalculateTotalSalaries method like this:

Even though this solution is going to give us the correct result, this is not an optimal solution.

Why is that?

Mainly, because we had to modify our existing class behavior which worked perfectly. Another thing is that if our boss comes again and ask us to modify calculation for the junior dev’s as well, we would have to change our class again. This is totally against of what OCP stands for.

It is obvious that we need to change something in our solution, so, let’s do it.

Better Salary Calculator Example – OCP implemented

To create a code that abides by the Open Closed Principle, we are going to create an abstract class first:

As a continuation, we are going to create two classes which will inherit from the BaseSalaryCalculator class. Because it is obvious that our calculation depends on the developer’s level, we are going to create our new classes in that manner:

Excellent. Now we can modify the SalaryCalculator class:

This looks so much better because we won’t have to change any of our current classes if our boss comes with another request about the intern payment calculation or any other as well.

All we have to do now is to add another class with its own calculation logic. So basically, our SalaryCalculator class is now closed for modification and opened for an extension, which is exactly what OCP states.

To finish this example, let’s modify the Program.cs class:

Awesome. We have finished our first example.

Let’s start with another one.

Filtering Computer Monitors Example

Let’s imagine for a moment that we have a task to write an application which gives us all the required information about computer monitors in our shop, based on different criteria. We will introduce only two criteria here, the type of monitors and the screen size. So let’s start with that:

To continue, we are going to create a simple model class:

Now, we need to implement our filtering functionality. For example, we want to filter by the monitor types:

And finally the Program.cs class:

This is going to work just fine. But, after a couple of days, we receive a request that our customers want to have the filter by Screen functionality as well.

So this should be quite simple, shouldn’t it?

Let’s just change the MonitorFilter class:

Even though this is going to give us the correct result, we have a problem because we have to modify our existing class. And what if we receive another request to filter all the monitors by type and screen together? We all see where this lead us, towards breaking the OCP. We are not extending our MonitorFilter class but modifying it.

So, in order to avoid existing class modification, let’s try another approach.

Creating a couple of interfaces is going to be our first step:

With the ISpecification interface, we can determine whether or not our criterion is satisfied and we can send it to the Filter method from the IFilter interface.

To continue on, we are going to create a separate class for the monitor type specification:

After this modification, all we have to do is to write a class that implements IFilter interface. But because we already have the MonitorFilter class, we are just going to modify it:

Finally, let’s modify the Program.cs class:

The result should be the same:

Open Closed Principle second example

Additional Filter Requests

Right now, we are perfectly able to extend our MonitorFilter class without any further modification. So, if now we have to implement the filter by screen feature, for example only widescreen monitors, we can do it with a new class:

And, we can make a call towards the MonitorFilter class:

Excellent.

With this project structure, we can even extend our filtering criterion to, for example, only OLED and widescreen monitors. All we have to do is to create another specification class.

Why Should We Implement the Open Closed Principle

By implementing the OCP we are lowering the chance of producing bugs in our project.

For example, if we have a fully working and already tested class in production, by extending it instead of changing it, we would definitely have a lesser impact on the rest of the system.

Therefore, we introduce another class to extend the behavior of the main class thus avoid the existing functionality modification that other classes may rely upon.

Another benefit is that we only have to test and deploy the new features, which wouldn’t be the case if we had to change existing functionality. Furthermore, if we decide that we don’t need this feature anymore (sometime in the future), all we have to do is to revert just newly implemented change and that’s it.

Conclusion

We’ve seen how the OCP can help us create better and more maintainable code. But, as with everything else, we should be cautious when implementing this principle.

Sometimes it’s just impossible to extend our class and all we are left to do is to modify existing functionality. We shouldn’t be afraid to do it, it is quite normal, but at least we should try to make those changes as discrete as they can be.

So, we should develop our applications with the OCP in mind and we should strive to write extendable code as much as we can because it leads to the maintainable, scalable and testable codebase.

And that’s what we want, isn’t it?

If you have enjoyed reading this article and if you would like to receive the notifications about the freshly published .NET Core content we encourage you to subscribe to our blog.