Answer to both questions is yes. When I read something like this I find the 'one size fits all' dogmatism disturbing. So, maybe someone somewhere created a class hierarchy that was needlessly complex. Now apparently the pendulum needs to swing all the way to the other side and all of inheritance is bad. I hear that lots of people are cutting themselves incidentally with knives. Shall we forbid them? In programming people don't seem to be able to settle on the middle ground, the pendulum always needs to swing to the extremes apparently.
An example where implementation inheritance is beneficial? I have a calculation that depends on quite a few parameters. The calculation can be done from the application code but part of the calculation is rather expensive and is only done once every so often. So, I put the parameters of the calculation in a class, inherit another class from that the the application uses and that can do cheap calculations. I inherit another class that does the complicated part of the calculation. All in all three classes so not very complicated. The disadvantage with making the parameter class a member is that the code gets littered with indirection like 'data->param1' all over the place. This is much nicer.
Isn't that a rather trivial concern? Implementation inheritance is not "much nicer" than delegation, the opposite in fact is the case. It adds a dispatch step to all calls to virtual methods (including calls that are private to implementations at any level of your 'hierarchy') that is not what you would want in most cases. Which in turn means your entire class hierarchy has to be analyzed as a single, highly-coupled program module; it's quite literally impossible to understand portions of it in isolation.
In cases I was describing readability really takes a bit hit. It is a bit like writing the abc-formula and having to write (- data->b +/- sqrt(pow(data->b, 2) - 4 * data->a * data->c ) / (2 * data->a). Being able to write (- b +/- sqrt(pow(b, 2) - 4 * a * c) / (2 * a) is so much nicer.
Actually, the three classes I described by themselves do not need any virtual methods. But I was skimping on details a bit. There actually is a fourth class that is being used to mock the application facing class and that causes some virtual methods to be necessary.
do_something :: (using data: Data, arg: int) { field1 = field2 + arg … }
Which lets you get rid of the annoying data.field1 and data.field2 indirection.
I really wish more languages implement this feature…
Can certain things be solved neatly with inheritance in a given language? Sure.
Is it a hammer for every nail? No.
For example, at work we use Delphi. Delphi has fairly excellent support for COM-like interfaces[1]. I prefer using interfaces, and I prefer to use composition where it makes sense, since Delphi has support for delegation[2].
But when implementing certain interfaces I might use inheritance through a base class with one or two abstract methods.
Could I have done the same through composition? Absolutely, but sometimes using inheritance leads to a smoother implementation. To me this is a happy middle ground, in Delphi. The resulting hierarchy is very shallow, almost never more than two levels. And the users are shielded from this implementation detail through the interface.
[1]: https://docwiki.embarcadero.com/RADStudio/Sydney/en/Object_I...
[2]: https://docwiki.embarcadero.com/RADStudio/Sydney/en/Using_Im...
You definitely don't want multiple inheritance of concrete types. I wrote a bunch of Python without ever realising it had multiple inheritance and I feel like that's a lucky escape, inheritance on concrete types causes problems, but multiple inheritance causes exponentially worse problems. Scenarios people have shown me of "good" multiple inheritance in Python all looked like regrettable mistakes.
No, but since Python allows multiple inheritance, one can use parent classes as traits for composition without having to write so silly boilerplate.
2. The modeling of taxonomies (Aristotolian).
3. Modeling fixed generalization-specialization relationships.
Commentary on an excerpt from an interesting blog series: https://softwareengineering.stackexchange.com/a/361176
Regarding the link, I remember Eric Lippert’s series of blog posts and his struggle to (initially) model a prototypical type problem using inheritance. I remember thinking at the time that he was limited in his object modeling experience to only the constructs provided by the C# language. There are no language constructs that specifically deal with rules relating to (optionally) collaborating objects, as he discovered.
https://doc.rust-lang.org/book/ch17-03-oo-design-patterns.ht...
We end up with three types of post, Post, DraftPost and PendingReviewPost, and our state pattern is implemented by the type transitions implemented in these three types. When you call Post::new() you get a DraftPost, and the only way to get a Post from that is to request the review, obtaining a PendingReviewPost and have somebody accept it to get the Post from there.