I have put decades of thought and effort into learning object-oriented software design deeply. I specialized in building object modeling tools in the beginning of my career.
I first read “Design Patterns” the year it came out.
Maybe you could let my perspective sink into your brain for more than three minutes before dismissing it out of hand.
If you simply dismiss everyone who offers you new perspectives, you rob yourself of the opportunity to learn new things.
On performance: Object creation and property access is very rarely your application’s bottleneck. If you really want to build “fast” apps, Google “RAIL performance model” and then get busy optimizing stuff that really matters, like page load times and smooth animations.
If I were writing a library like RxJS and I needed to maintain object instances, I might consider using `class` as an internal implementation detail, but I’d expose a factory function.
Good alternatives include ducktyping and structural type checking. Consider static analysis at compile time with TypeScript if this is a concern for you.
As for the “false / broken” examples, all class taxonomies, given sufficient time and changing requirements are eventually wrong for new use-cases. With single-ancestor “is-a” relationships, refactors to allow for new use-cases are complicated. Of course any example attempting to demonstrate that is going to appear to be a contrived, obviously wrong, “broken” design.
But every real-life example seems obviously wrong after the fact, too. The point is, we can’t predict changing requirements in advance, so we can’t engineer correct hierarchies that will always be obviously good design.
That’s the point. Proper OOD is impossible using single ancestor class taxonomies as your medium, but it’s easy with composition, hence the Gang of Four’s famous advice:
“Favor object composition over class inheritance.”