Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

The Open-Closed Principle, and What Hides Behind It

DZone's Guide to

The Open-Closed Principle, and What Hides Behind It

This is the second post I've written on SOLID principles and the common foundations they all have. Today, we look at OCP.

· Java Zone ·
Free Resource

How do you break a Monolith into Microservices at Scale? This ebook shows strategies and techniques for building scalable and resilient microservices.

This is the second post (check the Single-responsibility principle here) on SOLID principles and what common foundation they all have.

Bertrand Meyer, 1988

Here is what Bertrand Meyers writes in his book “Object Oriented Software Construction” where this principle originates from:

A class is closed, since it may be compiled, stored in a library, baselined, and used by client classes. But it is also open, since any new class may use it as parent, adding new features. When a descendant class is defined, there is no need to change the original or to disturb its clients.

So he proposed using an implementation inheritance. Not good at all.

Robert Martin, 1996

Poor Robert Martin is often blamed for what actually Bertrand Meyer proposed. No wonder, look what he wrote in his paper “The Open-Closed Principle”:

1. They [Modules, as well as classes] are “Open For Extension.” 
This means that the behavior of the module can be extended. That we can make the module behave in new and different ways as the requirements of the application change, or to meet the needs of new applications.
2. They are “Closed for Modification.” 
The source code of such a module is inviolate. No one is allowed to make source code changes to it.

I realize that a good part of readers has read no further than that and stigmatized Robert Martin for using implementation inheritance. Well, it’s just an unfortunate naming. What follows is the elaboration of what he meant. And he’s quite clear about that:

Abstraction is the Key.

He demonstrates a simplistic example of a Client class depending directly on a concrete Server class:

class Client
{
    public function doSomeWork()
    {
        return (new Server())->run();
    }
}

Obviously, when the  Server class will have to be replaced, Client is affected. So the moral of the story: introduce abstractions and let your clients depend on them, instead of concrete implementations. And it’s all about the correct decomposing of the problem space, resulting in a bit of abstraction and quite a few composable, loosely coupled, and highly coherent implementations.

On a module level, this principle is applied best with an approach David Parnas described back in 1972. It enforces high cohesion, while the open-closed principle suggests extracting abstractions, thus enabling loose coupling.

Robert Martin, 2003, 2004, 2013, 2014

All the following years he has been striving to make clear his point of extending the behavior not via inheritance, but via swapping implementation of useful abstractions. Not very successfully: in 2017, some still don’t get it.

Class Parameterization

I’ve listed two ways of introducing new classes without modifying existing code. The first one is using inheritance, which is discouraged, and the second one is extracting new interfaces so that they can be injected in a class. The third way is class parameterization if I can call it so. It’s very close to introducing new abstractions, but it’s about injecting data instead of behavior. Consider the following example. I have a class that calculates employers’ salary. There could be different strategies, and this is reflected as the following (an example is simplified to stress the point):

class SalaryService
{
    const TAX = 0.07;

    private $salaryStrategy;

    public function __construct(SalaryStrategy $salaryStrategy)
    {
        $this->salaryStrategy = $salaryStrategy;
    }

    public function calculate()
    {
        return $this->salaryStrategy->calculate(self::TAX);
    }
}

So when a new strategy emerges, I won’t need to modify a SalaryService class. It means that OCP is not violated with respect to this requirement. But then you’re told that a tax changed — so you need to reflect that in the code. Would it be a violation of the open-closed principle? Sure, since you need to modify existing code. What options do you have? Simple constant doesn’t represent any behavior, it represents data. Though you still can inject it via an argument, say, constructor parameter:

class SalaryService
{
    private $salaryStrategy;
    private $tax;

    public function __construct(SalaryStrategy $salaryStrategy, $tax)
    {
        $this->salaryStrategy = $salaryStrategy;
        $this->tax = $tax;
    }

    public function calculate()
    {
        return $this->salaryStrategy->calculate($this->tax);
    }
}

Violation of the Open-Closed Principle

The code above doesn’t violate OCP in the context of a newly emerged requirement. Basically, there is no such thing like just “violation of the open-closed principle.” It can be violated only for some set of requirements. So the first version of SalaryService could have been aligned with the open-closed principle for years, right until the moment when the tax changed.

Every time you need to implement new requirements and you modify existing code, it’s OCP violation. Of course, you can’t find out all your abstractions in advance — just because you can’t find out all the requirements in advance. So violating the open-closed principle is OK — only if you’re extracting new abstractions, of course.

Summing Up

If any behavior, which is a part of some class, can change, then its implementation should hide behind an interface. This has a name already — it is loose coupling. But it can’t come up purely mechanically, by extracting a couple of interfaces. It all starts with domain decomposing.

How do you break a Monolith into Microservices at Scale? This ebook shows strategies and techniques for building scalable and resilient microservices.

Topics:
open closed design principle ,oop ,coupling ,encapsulation ,java

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}