1. 程式人生 > >Maintainable Code and the Open-Closed Principle

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

to an object, but should never change the design of old 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

object to store its 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.