Maintainable Code and the Open-Closed Principle
The Open Closed Principle
Robert C. Martin, creator and chief evangelist of SOLID, credits Bertrand Meyer as the originator of the OCP. In his 1988 book Object Oriented Software Construction, Meyer describes the need to develop flexible systems that can adapt to change without breaking. To do this, Meyer advocates the design of systems where entities (classes, modules, functions, etc) are “open for extension, but closed for modification”. In his development of the SOLID principles, Martin runs with this idea, describing it as a “straightforward” attack against the threat of “fragile, rigid, unpredictable and un-reusable” code [1]. For his part, Martin breaks down the OCP into its two constituent parts, defining code that is “open for extension” as code to which you can add new behavior, and code that is “closed for modification” as code that is “inviolate” in that it’s design should never be changed once implemented. In other words, the OCP says that you can always add new code
The chief benefit of the OCP is maintainability. If you adhere to the OCP you can greatly decrease future maintenance costs. The opposite applies as well — when you don’t adhere to the OCP, future maintenance costs will be greater. Consider how the coupling of two entities affects their respective maintainability. The more a given entity knows about how another one is implemented, the more we can say that they are coupled. Therefore, if one of the two entities is changed, then the other must be changed too. Here is a simple example:
In this snippet we have a simple function called announce
that takes an object as an argument and uses that object’s items
and description
properties to log a message to the console. When we call this function and pass it the favoriteCities
object we get the expected output. But what if we decide that we don’t want the favoriteCities
items
in an array and decide it’s better to store them in an object?By changing our favoriteCities.items
implementation from an array to an object we effectively broke our announce
function. The reason is that the announce
function knows too much about how favoriteCities
was implemented and expects it to have an items
property that is an array. Fixing this would be relatively trivial (perhaps we could add a conditional to the announce
function to check first whether the collection.items
property is an array or an object), but at what long-term cost? What if we didn’t make this change until much later in development and we had lots of functions that used collection.items
? We would then have to add conditionals to every place that referenced items
.
A better solution is to use polymorphism and to let each collection
object decide for itself how its items
should be iterated over and logged. In this pattern, the announce
function doesn’t care whether the collections it works with use arrays, objects, or some other data structure to hold their items
. Here is one approach:
In this final snippet, we provide favoriteCities
with a logItems
method that implements how to log its items. As far as announce
is concerned, it can deal with any collection object so long as it has a description
property and a logItems
method. This is the OCP in action — the announce
function is extensible because it can handle any collection
that guarantees these two properties but it is also closed to modification because we don’t have to change the source code in announce
to change its available behaviors.