You're not doing justice to the open/closed principle.
The open/closed principle is a lot more subtle than just "inherit from existing classes instead of modifying them".
Actually, you can write open/closed compliant classes without using inheritancy at all (e.g: C++'s std::list, which allows injecting the allocator and the ordering function).
I agree, in the general case, it's impossible to write code up-front that is "closed" in an absolute way, i.e which would resist to any kind of feature request.
Trying to do so will result in horribly over-generalistic code.
However, you can still spot parts of your code that always grow a little in response to some kind of feature requests (e.g each time you add a new input format, you need to add a new 'case' in this function). Then, you can decide to "close" your class against these kind of feature requests (e.g injecting the list of supported formats at construction).
I'm going to be somewhat controversial and say that I think the open/closed principle is actually a bad idea. I say that slightly carefully because whenever I shoot my mouth off on a topic that most smart people agree about, usually I find that I've misunderstood something. However, if you will indulge me...
I think the intention of the open/closed principle is to stop people breaking the back of the camel one straw at a time. It's tempting when you get feature requests to think, "Oh, it's easy. I'll just extend this class". Clearly this is something that you don't want because eventually you have a monster class with no clear purpose.
However, it is almost as easy to break the back of the camel by piling on functionality (either through inheritance or through composition). Now, you have code that is just as coupled, but we have lost the cohesion. Instead of being crappy code crammed into a single monster class, it is crappy code strewn all through the system making it virtually impossible to find.
I think the parent post is closer to the truth of what you actually want. When you get new requirements, you want to refactor your design. It should be open to obliteration and closed to a death of a thousand cuts.
The main problem with the open/closed principle, from my perspective, is that it perpetuates the idea that we should try to minimise code churn. Many people will read that and think, "Well, duh. Code churn is dangerous." But every time you minimise code churn, you add a design constraint. At best, this design constraint will lead to the pile of straw on the camel's back. At worst, the workarounds imposed by avoiding change necessitate more workarounds, slowly adding exponential growth to the complexity of the system.
What I would like to replace the open/closed principle with is a principle that says "Do not nail your APIs to the wall". They must change, if you are to maintain simple code in the face of changing requirements. Sometimes you must solidify an API, but you should always delay that decision as long as possible and you should limit the extent as much as you can.
The open/closed principle is a lot more subtle than just "inherit from existing classes instead of modifying them". Actually, you can write open/closed compliant classes without using inheritancy at all (e.g: C++'s std::list, which allows injecting the allocator and the ordering function).
I agree, in the general case, it's impossible to write code up-front that is "closed" in an absolute way, i.e which would resist to any kind of feature request. Trying to do so will result in horribly over-generalistic code.
However, you can still spot parts of your code that always grow a little in response to some kind of feature requests (e.g each time you add a new input format, you need to add a new 'case' in this function). Then, you can decide to "close" your class against these kind of feature requests (e.g injecting the list of supported formats at construction).