1

I am confused about the Open/Closed principle. The principle says "open for extension, closed for modification". My thinking is that if there is a class and if you need to add any new functionality to that class, you can add that functionality by creating a new derived class which is inherited from the base class. So you add the functionality required without any modification to the base class.

In a similar fashion, the OCP principle can be obeyed by applying the above methodology to any class. Is that true?

  • I see many answers below, but I think we can understand OCP in your way. It's easy to remember – Jack Sep 1 at 10:44
1

A class (or a collection of collaborating classes, or even a function) can be designed to make use of the Open-Closed principle, but the key thing to understand is that this isn't a magic bullet. No class can magically be made open-closed against all kinds of changes imaginable.

So you have to decide for which kinds of changes it should be open (and conversly, which kinds of changes it will not easily support). That is, the users of the class (client code) rely on some abstraction (represented by an interface or a base class) that the extensions can implement or derive from; for this to work, it has to be possible to create these extensions without altering the abstraction.

It's not always easy to figure out what the design should be from the start. Sometimes you can make a good guess, or base your decision on previous experience, but other times you have to be prepared to restructure the code later on, once you have a better understanding of how the system changes, and what are the forces driving the change - so that you arrive at a structure that makes those kinds of changes easy (that is open against those kinds of changes).

An example would a framework that lets you derive a class from a base class it provides, that is then instantiated and "plugged" backed into the framework. (You may have used some framework where you create MyForm that derives from Form, or MyController that derives from Controller, or some such thing; the interfaces of those base classes are closed for modification, because the framework relies on them to interact with your application, but what the application actually does is open to extension - by you. You don't have to touch the framework's code at all, and your own code is in a different compilation unit.)

Another example that doesn't involve classes is a function that lets you filter a collection. E.g, in C# there's LINQ's Where method, in JavaScript there's a similar method called filter - both work on a collection of elements, and take as a parameter a user provided function that tells the method what to keep and what to filter out. The method is closed against the changes to the signature of that function (it must take an element of the collection and return a bool), but is open for extension by different filtering strategies - any method that has the given signature will do, as long as it has enough information to determine what to keep and what to discard. The abstraction here is the signature of the user-provided function; the code that does the actual filtering doesn't have to be changed for different filtering strategies. A user-provided function that has the required signature essentially "implements" that abstraction.

Some abstractions, such as this one, have a more universal utility, others are more specific to the problem domain.

| improve this answer | |
1

My thinking is that if there is a class and if you need to add any new functionality to that class, you can add that functionality by creating a new derived class which is inherited from the base class. So you add the functionality required without any modification to the base class.

I think I see a pretty common misconception here about OCP. IMHO the OCP is not saying to use inheritance exclusively to add new functionality to a class, and it is not saying when it comes to implement a new functionality, one should avoid to change the existing code (to be fair, Uncle Bob once wrote something like "Though shalt not modify this code", but I think it is best not to take this too literally).

The OCP is a principle which is applied when a class is created first, to design it in a way new features can be added later without modifying the code. The act of reusage without modifying the existing code is a result of the OCP, not its application.

This is most simple to understand when you think in terms of general black-box libraries, provided by some 3rd party vendor. Those libraries may contain several reusable classes, and when you can use and extend them without the possibility of changing their source code, then the vendor of that library has applied the OCP successfully (and not the one who uses the lib).

| improve this answer | |
  • 3
    When some uncle tells you to do something, never take it literally. An awful lot of damage has been done that way. – gnasher729 Mar 11 at 14:26
1

I'll tell you what this is all about, because that makes it a lot easier to understand instead of understanding some unmotivated "principles".

Let's say you have a class A, and it is used by 5 users, and has two subclasses. Now one of the five users decides they need class A to behave slightly different, so he modifies the class.

Bummer. We now have four users and two subclasses all with broken behaviour because they didn't want that change. When a class is used like this, you can't just change it. It is closed. (Of course you can change it but you would have to go through all their uses first and make sure everything works as it should, which is a lot of work).

So that one user decides to instead make a subclass, and implement his changed behaviour in the subclasses. That's better because it doesn't break anyone else's code. Still, you have one more subclass. And having more subclasses isn't good, it increases maintenance cost.

Better is to have a class that is extensible. That can be made to do what I want (instead what you want) by leaving the class unchanged, but making it possible to change its behaviour from the outside.

As a very obvious example, if you have a class "Button" putting a button on the screen, you don't want to make a subclass "GreenButton", "RedButton" and "BlueButton" for when you want a green, red or blue button. He want the button to have a property "color" that can be used to make it display any color you want, without changing the class, and without subclassing.

If you planned ahead, your class is already ready to be modified this way (on the other hand there is the "You Ain't Gonna Need It" principle which says you shouldn't add capabilities to your class that you aren't going to need). If you didn't plan ahead, you add the ability to be extended to the class, making sure that the behaviour is unchanged if that particular ability is not needed.

As in everything, use common sense. If you can extend a class so much that it is unrecognisable depending on who uses it, then perhaps you are doing something wrong. And if you are the creator and also the only user of a class, do what you like. Until you are not the only user anymore.

| improve this answer | |
  • Making a class extendable by parameters is indeed a valid approach to follow the OCP, I think that is an important fact which gets often overlooked. However, designing objects or classes in a way where the intended way for extensiblity is polymorphism and/or inheritance is not inherently against the OCP (and this answer bears a certain potential to be misread that way). – Doc Brown Mar 11 at 14:48
  • Oh! Oh! Don't stop there. Passing static values that customize the behavior of the button in limited, pre-defined ways (e.g., background color) is all right, but where it really gets interesting is when your button class can delegate more complex behaviors to client-supplied objects. – Solomon Slow Mar 12 at 17:16
0

You are partly correct. Inheritance and interface can help achieve this. Given said that, you don’t have to add a new derived class for every new functionality added. You might be unnecessarily complicating your project.
Considering “method level”, the principle says Open - extend existing code to add new methods, closed - once method is developed and tested, it must be touched only to fix bugs
In a class-level:
Bertrand mayer told

A class is closed, since it may be compiled, stored in a library, baselined, and used by client classes. But it is also open, since any new class may use it as parent, adding new features. When a descendant class is defined, there is no need to change the original or to disturb its clients.

Example of this is usage of inheritance/interface for polymorphic calls to various implementations instead of lengthy “switch-case” statements which need to be modified when there is a new implementation
Please watch Uncle Bob’s lectures on SOLID principles. They are very useful since they have clear examples of usage.

| improve this answer | |
0

My thinking is that if there is a class and if you need to add any new functionality to that class, you can add that functionality by creating a new derived class which is inherited from the base class. So you add the functionality required without any modification to the base class.

That's a reasonable (if oversimplified) description of OCP, but beware of one major caveat: new functionality shouldn't always manifest as inheritance. Depending on exactly how different this new functionality is, it might be better to instead offload this new feature onto a different (non-derived) class altogether.

This is a matter of SRP. Indirectly, SRP also extends to derived classes, more precisely to not foist a completely different responsibility to a newly created derived class.

I say "completely different", because your current question leaves it quite vague as to how different this additional feature is. If it's a functional variation on the existing class, then inheritance (or interface implementation) is appropriate.
If it's an unrelated feature, it doesn't belong in a derived class for the same reason it doesn't belong in the base class either.

| improve this answer | |
0

In a similar fashion, the OCP principle can be obeyed by applying the above methodology to any class, Is that true?

Nope. If you doubt it, try applying this methodology to the most popular class in existence: String

Not every class is designed for inheritance. Sometimes for damn good reasons. String likes being final and immutable. It doesn’t react well when you fiddle with that.

Now sure, you could use some other methodology where you create a collection of letters. You could even back it with a String but nothing that demands a real String will accept this substitute.

OCP is something you should think about before you paint yourself into a corner like this. Not later.

If you really had a need to override some string behavior since OCP has been ignored then you’d have to change impacted code that mentions String. I gotta say, ouch.

Yet String is wildly popular. Why? Because you can hide it behind abstractions that do follow OCP. If you have reason to suspect your implementation will need to change then pass around domain meaningful types not Strings.

The principle that asks you to do that is called primitive obsession. Just because an object uses a String doesn’t mean everything needs to know that.

Anyway, OCP must be planned for and applied wisely. Not obsessively. Don’t expect that you get it for free.

| improve this answer | |
0

Well tested code has more value than untested code. I don't mean unit tests here. I mean users using the app for years in unexpected ways and you have a class that is crucial that has stood the test of time. Imagine the signs one can see at factories "N days without incapacitating accidents"?, so such a class has been working superbly for years in production.

Now you have a special case, and exception to accommodate for some particular case or have to specialize the class for some reason.

Instead of changing code that has "N days without incapacitating accidents" you create a subclass that overrides a method or adds a new one, or even overloads a method, if violating LSP is not crucial here.

You don't have to risk introducing bugs in a stable class. You have to test the new class but not the old one. I mean of course you have a good battery of tests but again...

Real world example, imagine that versions are separated by months or years:

  • Ver 1: Tank class goes into production
  • Ver 2: Floating roof tank added (extends Tank)
  • Ver 3: Refrigerated tank added (extends Tank)
  • Ver 4: Pressurized tank added (extends Tank)
  • Ver 5: Refrigerated tank is improved to receive an array of temperatures to calculate an average instead of one single temperature, setTemp(double temp) is overloaded by adding setTemp(double[] temps)

The calculateVolume() method is overriden in every subclass since algorithms vary. 75% of the tanks in the company are of the Plain Base Type than has hardly changed.

One realizes that passing an array of temps even with a single item does not harm other tanks so the new setTemp(double[] temps) is promoted to the base class very cautiously and setTemp(double temp) is cautiously marked as deprecated, regaining LSP.

Thanks to OCP, the app evolutioned in an organic way, incrementally. But even such mature process can lead to the base class being improved and some old method marked as deprecated for future integrators to gradually stop using it.

| improve this answer | |

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