SOLID in Action — the Open-closed Principle

“A software entities (classes, modules, functions, etc.) should be open for extension but closed for modification”

Gerald Nguyen
Gerald Nguyen
5 min read ·
Previous | Next
Also on Medium
On this page
Photo by Richard Balog on Unsplash

Photo by Richard Balog on Unsplash

In the physical world, a door closed is a closed door. In the software world, a closed entity may still be open for extension. And it should be so, according to the Open-closed principle (OCP).

There are 2 popular ways to apply this principle. We are going to discuss them both.

And a third option.

#1 — Inheritance

This is the original application of OCP. Bertrand Meyer, who introduced the term in his 1988 book Object-Oriented Software Construction, intended that:

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.

Car and its associations

Car and its associations

Car is closed because its source code is not available for modification, or because it is used by other client classes which would not approve of any changes.

But Car is open for extension. Extending Car is in fact easy. A sub-type, ElectricCar, can extend from its parent classCar, supplying the actual values to themodel and seats properties, and adding a new batteryLevel property and a newrecharge() method.

#2 — Polymorphism

The 1990s saw the OCP re-interpreted to promote polymorphism via abstraction and implementation. According to Robert C. Martin’s “the Open-Closed Principle” article:

Abstraction is the key

Abstraction, according to Uncle Bob, is closed for modification yet open for extension through its unbounded implementations.

Consider the abstraction provided by Car. It is fixed and therefore closed. As far as UrbanTripPlanner concerns, Car provides 2 properties (model and seats) and 2 methods (getSpeed() and getPosition()).

Implementations of Car, either directly e.g. ToyotaVios or indirectly e.g. TelsaModel3, extend this abstraction with concrete information and behavior of a specific car’s model, number of seats, current speed, and current position. Thus, Car is open for extension.

Compare with Inheritance, this interpretation of OCP focuses on the extension of concrete implementation to the declared abstraction. All extra properties, methods, or types introduced through Inheritance are ignored.

OCP via Abstraction is broader and more robust.

Let’s expand our view beyond Car to includeUrbanTripPlanner and its implementations. If they know anything about EletricCar, then the behavior declared in the method searchDirection(Car, Position, Position) would violate the OCP principle, as it must be modified, thus no longer closed, to support the new type of Car. To avoid this violation is to ignore the identities of all Car’s sub-types and their extra properties and methods. That effectively trims Inheritance’s effect to just implementing Car’s abstraction.

In addition, with abstraction, we enjoy much built-in support in Object-oriented programming languages and modern IDEs. Forgetting to implement a method? it is a compilation error. Wanting to accept a list of Car in a type-safe manner? Generic List<? extend Car> to the rescue. In our example, becauseUrbanTripPlanner only cares for Car, as long as ToyotaVios, TeslaModel3 implement Car, our program is fine.

A Car abstraction — By DALL-E AI

A Car abstraction — By DALL-E AI

#3 — Built for extension

We have seen how Inheritance and Abstraction help us achieve the OCP. These two mechanisms are general tools determined by our choice of programming language or design decisions.

In this section, we will explore the third approach to OCP in which the mechanism is more nuts-and-bolts to us. It is either directly coded in our program or indirectly provided by a library or framework that we use.

Plug-in, Add-on, Add-in

These are simply different names for the same concept. According to Britannica:

plug-in, also called add-on or extension, computer software that adds new functions to a host program without altering the host program itself

Plug-in is thus an obvious example of openness in built-for-extension host programs.

Different types of software exist that depend heavily on extensions. For us developers, this should be intimately familiar as we use productivity and language plugins on a daily basis in our favorite IDE such as VSCode, Eclipse, and IntelliJ. Some browser extensions are invaluable to internet users. Plenty of emoji packs and filters exist to enhance the experience of mobile users…

Photo by Ferenc Almasi on Unsplash

Photo by Ferenc Almasi on Unsplash

APIs, Events

A related, but merits its own sub-category, under build-for-extension is API and Events.

The best-known extensions under this sub-category are HTML, CSS, and javascript codes. They all extend the functionality of the browser without modifying the internal of their host. They are made possible through the availability of API and Events defined by the browser’s programmable interface. Standardization efforts exist and have largely driven the development of the current Web 2.0 and Web 3.0 landscapes.

https://commons.wikimedia.org/wiki/File:WHATWG_DOM.png

https://commons.wikimedia.org/wiki/File:WHATWG_DOM.png

Conclusion

The OCP is certainly among the most important principles in Software Engineering. It provides protection against unwanted, inconsiderate changes yet allows extension when necessary.

We have visited two traditional ways of applying this principle: Inheritance and Abstraction. Arguably, Abstraction is preferred with a broader impact and more robust effect.

We also examined a third option for applying the principle. There are 2 prominent sub-categories, Plugins and APIs & Events under this approach.

If you like this article, please follow me for more quality content.

Other articles in this series:

Thank you.