This, basically. People should stop thinking about "composition vs. inheritance" and start thinking of implementation-inheritance and thus OOP in a strict sense as "composition plus open recursion". Open recursion (viz. the fact that methods defined on your "base" classes can invoke 'virtual' methods on self and end up depending on behavior that may have been overridden at any level in the class hierarchy) is quite often a misfeature and not what you actually want, given that the invariants involved in making its use meaningful or sane are extremely hard to usefully characterize.
Open recursion is pretty much core to the most useful OOP idiom, which I would assert is UI component toolkits.
UI heavily relies on a large number of conventions that the user learns to expect to cat in certain ways. You can parameterize a UI widget that encapsulates these conventions with function pointers or overridden methods, but the end result is pretty much the same.
I'll strongly agree however that far too few programmers in OO languages pay enough attention to the hard problem of designing for extension. It's the reason that C#, unlike Java, defaults to non-virtual methods.
Except UI trees are probably the worse place for objects
In PHP for example all query builders are meant to be used as a tree of objects, which means if you want to recursively change the table names used by the query you're out of luck because that state is often private and behaviour not implemented
Or maybe you want to inspect the query as data at runtime or assert against its shape or deeply update or delete or copy or compose parts of it all of those things are difficult
See hiccup for a data oriented UI HTML tree, honeySQL for SQL there's one for css, routing, there's some for all of the state of your app like redux and Integrant
As soon as you have a tree of objects your coworkers won't know how your objects work till you train them and even then it won't be as powerful as your standard library, but your coworkers do know how to manipulate arrays vectors maps sets dictionaries etc self made objects are obtuse in these use cases
VB, Delphi and WinForms have all been pretty successful in their domains. You'll need to work harder to convince me, and many other people, that their use of objects was the worst. The objects are part of the standard library, that's practically a given when talking about OO UI.
Objects are a bit weaker for UI as a projection of a domain data model, especially when it needs to be reactive to changes in the underlying data. The more the UI is data-bound, the more I prefer a functional / data flow idiom.
When the goal is to model something “in real life” OOP tends to map decently well and it’s easy to teach. When you’re trying to make sure your program isn’t going to go off the rails, limits on mutation is one of the first places to look. When your software has 50mm+ valid states, one should hopes they have a large manual QA team. If you have all the possible states held in their own subsystem, you can automate stability much more easily.
Classical OOP forces you to organize around a single and very specific taxonomy; things "in real life" are usually the very opposite of that. I remember textbook examples of inheritance with shapes or animals, both of which actually show clearly why it's a bad idea.
>>I remember textbook examples of inheritance with shapes or animals
Man, I wonder how people went through this wondering, Who uses this? Can't they show us something real!
Eventually you just kind of tune out, because when you often meet the real world use cases, OO either descends to hierarchical verbosity from hell with layers and layers of generic abstract stuff when things should be more direct.
Why is modelling in OOP any more "real life" than with other programming paradigms? I've heard this many times from OOP zealots but I just don't get it. Most examples of this I've seen focus on physical objects such as cars which is just ludicrous as your average piece of software is morel likely to be dealing with a data structure, such as a user profile, than anything physical.
My favorite example is SimCity. Any time you have a bunch of models that operate mostly independently and somewhat based on neighbors OOP seems to map nicely. When you are taking more abstract concepts or data flows (which is... probably 95% of web programming and 80% of all programming for example) it doesn't map well, and you end up with a lot of natural funkiness because the base modeling language doesn't match the concepts.
Yes, I know, Tim Sweeney has also got an interest in PL theory.
Nonetheless, that was in 2013, it's now 2019, video games are still written in C++ and not any FP language. FP has been "the future" for as long as I've been alive and I don't think it'll ever happen.