2

I typically make all my methods private unless and until they need to be overriden by a sub-class. At that point I modify the base class to make that particular method protected virtual. Is it accurate to say that this is a violation of the "Closed to Modification" principal? If so, that would imply that I should choose protected virtual as my default (similar to Java, in which methods are virtual by default). Is that really what the OCP recommends?

Personally, such an interpretation seems a bit daft to me, but I want to know if I am missing something.

  • 6
    (Personal opinion.) The entire SOLID principles has an unspoken focus on releasing software as libraries or packages. "Modification" refer to situations where a missing feature can only be fixed (added) by modifying the library/package, thereby re-releasing it, which increases the maintenance cost. If it were "extensible", the application that needs that missing feature can do it themselves. The principle can probably be viewed from many different angles; here's my two cents. – rwong Sep 29 '15 at 22:12
6

First, I think there's a fundamental difference between initial development (here of a base class) and later stages of development.

If you find yourself in the initial development category, you will often need some refactoring to make the base class accommodate its first subclass (sometimes add a parameter, split a method, etc...), so regarding a need refactoring, I wouldn't worry about the OCP at such an early stage.

Further, we cannot and should not try to foresee all possible and potential future uses of something; doing that is an anti-pattern in itself.

Also, there's a fundamental difference between internal classes and externally exposed classes. In the case of internal classes, they may well be all versioned together (e.g. in the same DLL), so there is no reason to disallow refactoring to accommodate new sub classes as the design evolves.

However, for externally exposed APIs that have the maturity of a number of sub classes (or third party plugins) already implemented (which also can be expected to be added to in different versioning units (e.g. different DLLs), one should take the OCP seriously: a better design will accommodate more third party sub classing without changes.

However, regarding externally exposed API's, we're better off using interfaces instead of sub-classing anyway. Subclassing seriously limits an implementors options by comparison with interfaces, while also complicating the interactions & dependencies between producers and consumers. Using an interface, we simply avoid the issue of whether to mark a method as virtual or not. When we choose interfaces, designs become more decoupled, all of which is the more in the spirit of the OCP.

  • 1
    Definite +1 for pointing the OP in the direction of interfaces rather than base classes. Let's kill off the use of inheritance one SE answer at a time... – David Arno Oct 1 '15 at 8:38
3

I typically make all my methods private unless and until they need to be overriden by a sub-class. At that point I modify the base class to make that particular method protected virtual

That is seldom a good tactics. When you create a class initially, you should make all methods not be exposed private. Fullstop. Those methods won't become protected later - they are typically not proper designed to be overridden later on, because when you created the method, their intended purpose was not to be an extension point. Making all of them protected virtual by default won't change that - just the fact you can technically override a method does not mean this will make any sense. Classes, which are intended to be instantiated on its own are seldom good candidates for the becoming base classes of other classes, and trying to switch their private methods to "protected" is not just a violation of the OCP, it leads most probably to bad or wrong usage of inheritance.

On the other hand, if you design an abstract class A in "top down" manner for which you know it will become a base class for classes B, C and D, because A is some kind of generalization of B, C and D, you will typically know right from the start which methods in A must be protected to be overridden by B, C, and D, so these methods are protected or public right from the start, and they are designed to be overridden.

You can create such a design also "bottom up": you start with a class B, then create a second class C, you see the commonalities in both and refactor the common methods to a common base class or common interface A (instead of trying to make C an derivation of B, in a wrong attempt to follow the OCP). Now the methods in A which need to be overwritten in B and C are all public or protected right from the start. B and C have to be modified, of course, but B and C are not the classes which are extended - so asking for the OCP is pointless here. However, ideally A does not have to be modified later when another class D inherits from A. If that's the case, you A confirms to the OCP.

So the bottom line is: classes which follow the OCP are not created out of existing classes just by making any method "protected" - the "extension points" have to be chosen deliberately and designed for that purpose.

  • Great points, thanks! But consider this: I've got an Encabulator class which knows how Encabulators should function. Client says, "Now we want it backwards compatible with the previous version of DingleArm_v1. I don't want to muck up the Encabulator with special-case code logic. (I could make an IEncabulator interface, but that seems too intrusive.) My solution: sub-class Encabulator (in this case Encabulator_v1) and override the methods that need different logic. So now, when User needs compatibility with DingleArm_v1, I inject Encabulator_v1. Seems elegant to me. Your take? – kmote Sep 30 '15 at 14:33
  • 1
    Elegant? Honestly, this looks more like a quick-and-dirty hack (not that I have not done this in the past by myself ;-) You will have definitely to change something in your Encabulator - you must be extremely lucky if the parts you need to change are exactly mapped to methods you can simply switch from "private" to "protected". Moreover, you should consider if inheritance is the right tool here. Most probably you can refactor the "special case logic" necessary for DingleArm_v1 inside of Encabulator into a separate helper class, and inject that helper class as a parameter from outside. – Doc Brown Sep 30 '15 at 14:51
  • 1
    That helper class then gets an interface with different subclasses. That will typically improve the design of Encabulator and avoid problems when the next requirement is to have an Encabulator compatible with FooArm, one compatible with BarArm, and one compatible with both (if you end up having Encabulator_DingleArmV1, Encabulator_FooArm, Encabulator_DingleFooArm, Encabulator_DingleFooBarArm, then you know that something is wrong). – Doc Brown Sep 30 '15 at 14:56
0

Inheritance is OKish mechanism for fulfilling OCP. I would however point out that OCP+inheritance can break LSP. That's because inheritance poorly support orthogonal behaviors/responsibilities.

However plain old composition can also fulfill OCP while keeping LSP happy ;) One such example would be strategy pattern. Another one would be parametric polymorphism (aka. generics).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.