Design – How to Adhere to the Open-Closed Principle in Practice

designobject-orientedobject-oriented-designsolid

I understand the intent of the open-closed principle. It's meant to reduce the risk of breaking something that already works while modifying it, by telling you to try to extend without modifying.

However, I had some trouble understanding how this principle is applied in practice. To my understanding, there are two ways to apply it. Beofore and after a possible change:

  1. Before: program to abstractions and 'predict the future' as much as you can.
    For example, a method drive(Car car) will have to change if
    Motorcycles are added to the system in the future, so it probably
    violates OCP. But the method drive(MotorVehicle vehicle) is less
    likely to have to change in the future, so it adheres to OCP.

    However, it's quite difficult to predict the future and know in
    advance what changes are going to be made to the system.

  2. After: when a change is needed, extend a class instead of modifying it's
    current code.

Practice #1 isn't hard to understand. However it's practice #2 that I'm having trouble understanding how to apply.

For example (I took it from a video on YouTube): let's say we have a method in a class that accepts CreditCard objects: makePayment(CraditCard card). One day Vouchers are added to the system. This method doesn't support them so it has to be modified.

When implementing the method in the first place we failed to predict the future and program in more abstract terms (e.g. makePayment(Payment pay), so now we have to change the existing code.

Practice #2 says we should add the functionality by extending instead of modifying. What does that mean? Should I subclass the existing class instead of simply changing it's existing code? Should I make some kind of wrapper around it just to avoid rewriting code?

Or does the principle not even refer to 'how to correctly modify/add functionality', but rather refers to 'how to avoid having to make changes in the first place (i.e. program to abstractions)?

Best Answer

Design principles always have to be balanced against each other. You can't predict the future, and most programmers do it horribly when they try. That's why we have the rule of three, which is primarily about duplication, but applies to refactoring for any other design principles as well.

When you only have one implementation of an interface, you don't need to care much about OCP, unless it's crystal clear where any extensions would take place. In fact, you often lose clarity when trying to over-design in this situation. When you extend it once, you refactor to make it OCP friendly if that's the easiest and clearest way to do it. When you extend it to a third implementation, you make sure to refactor it taking OCP into account, even if it requires a little more effort.

In practice, when you only have two implementations, refactoring when you add a third is usually not too difficult. It's when you let it grow past that point that it becomes troublesome to maintain.

Related Topic