September Writing Challenge, Post 15: The O in SOLID

Note: This is the second of five posts I’m writing on the SOLID principles of object-oriented programming. Part 1: S

The Open/Closed Principle says that software components should be open for extension, but closed to modification.

Sometimes you will see this described in terms of inheritance. Drawing from the examples in my post yesterday, take the case of a retail product that has multiple possible JSON representations exposed by a web API. The product detail API call will give you everything you need to fill in a product information page. Looking up order history, though, you’ll have some part of that same information, plus a quantity, an order price (which may be different than the current purchase price), &c. For the sake of argument, let’s also say you have a well-tested ProductMarshaler class that handles data received from the product detail endpoint. In Swift, you might have something like (assuming the JSON has been parsed into a dictionary):

But what about the order API response, which needs something extra? We could go and alter the existing ProductMarshaler… but that feels uncomfortably close to a violation of the Single Responsibility Principle. It also feels icky to muck around in tested, working code. But we don’t want to duplicate everything from the existing marshaler either, because duplication is bad, m’kay?

We don’t need to alter the stable, working portion of the system in this case, we just need to extend it. In OO languages, one way to do that is with inheritance:

We have closed off a stable part of the system to changes, but taken advantage of the fact that that part is open for extension. We’ve taken advantage of common behavior while specifying only the extra bits we need for our special case.

Inheritance isn’t right for every case, though. What if you’re working on a payroll system that requires you to draw employee data from both a sexy, new JSON-based API and an old and busted XML-based API? Inheritance doesn’t buy us much here – there’s no obvious, natural way to draw common functionality out of the tasks of extracting data from these two formats… But the one thing they do have in common is that you’re passing in a string representation and getting back an Employee model object. Most modern OO languages allow you some way to define an interface – that is, a sort of contract for your object’s behavior – without specifying an implementation. In Java, that might look like:

And your classes would look like:

…and similarly for the JSON marshaller.

In both cases, when we say the class implements an interface, we’re forcing it to commit to a contract: “I’m going to take in a string representation and return an Employee object and send you errors out-of-band.” The interface is the part that we have closed to modification; we’re saying that all our employee marshallers must commit to this contract. Implementation is left open; the newer JSON implementation (which, being new, might still be seeing changes to its requirements) can change freely without impacting the legacy XML implementation.

If you read articles about SOLID and Open/Closed on the web, you’ll see both interpretations of the principle, inheritance-based and interface-based (with the interface-based interpretation being newer and hipper). Both are useful. There may be other interpretations, as our thinking and our computing languages evolve. The most profitable way to think about the Open/Closed Principle is to ask yourself: What are the parts of the system that are likely to change very slowly, or not at all? Whether those are interfaces or implementations, those are the things that you should figure out early, and close to modification. These stable, closed parts of your system should be the coupling points between the “open” parts of your system. With interfaces especially, those closed portions of your object design can be used to segregate regions of change and technical risk, so that they remain tractable in the face of requirements changes and debugging.

And speaking of interfaces, they’ll figure heavily into tomorrow’s post on the Liskov Substitution Principle.

Leave a Reply

Your email address will not be published. Required fields are marked *