From a technical aspect,
From a technical aspect, you are totally right that you can beautifully compose objects by means of inheritance, if your programming language supports multiple inheritance.
But from a technical aspect, the Java-style single inheritance is very limiting, and for many software developers, this is the only kind of inheritance they’ll ever get to learn in their college.
Moreover, it’s very hard to get the design right using this kind of inheritance.
Mattias Petter Johansson has explained in his awesome video and article Composition over Inheritance, and unarguably, Mattias is also talking about the technical problem of single inheritance, where you are unable to fit a MurderRobotDog into the inheritance chain.
But that’s only from a technical aspect and did not cover the full picture.
From an object-oriented analysis and design aspect,
One interesting point is how Mattias defines inheritance.
Inheritance is when you design your types after what they are, while composition is when you design your types after what they can do.
There’s one thing I know from this definition.
This has absolutely nothing to do with any programming language, whether single or multiple inheritance, but it has to do with how one approaches OOAD.
Of course, in languages that support multiple inheritance like Python, you can compose objects through inheritance, and when it’s done right it is very beautiful.
But to the untrained minds, even with multiple inheritance, they could’ve modeled objects based on what they are and ended up like this (again, borrowing Mattias’ excellent example):
def drive():class CleaningRobot(Robot):
def clean():class MurderRobot(Robot):
def kill():class Animal:
def poop():class Dog(Animal):
def bark():class Cat(Animal):
class MurderRobotDog(MurderRobot, Dog):
Use Python — technical problem solved.
And you’re right that you are actually composing a MurderRobot and a Dog to form a MurderRobotDog.
But now you are left with the banana-gorilla problem. Now your MurderRobotDog has a poop() method, which it should’t.
Now, you can see that it no longer has to do with what composition and inheritance technically means, in terms of a programming language.
No, it has nothing to do with whether you use single or multiple inheritance, object composition, stamps, mixins, factories, or whatever.
Rather, it has to do with how you think about objects. It is about how you design your types.
That’s right, you can use the Stamp specification, and still end up creating that banana-gorilla problem unless you stop modeling your types based on what they are.
On the other hand, if you model your types based on what they can do, you’ll end up with an elegant, beautiful, and natural solution in a language that supports multiple inheritance:
class MurderRobotDog(Driver, Killer, Barker):
I’m so tired of these stupid ambiguous terminology.
You see, ambiguity creates confusion.
To anyone reading this,
Is there any way to explain these different technical and OOAD concepts using clear and unambiguous terms?
That’s what I believe the Stamp specification is trying to do. Creating an entirely new way of stamping out new objects is just a false front, because we all know we can achieve the same using mixins, multiple inheritance, higher-order classes, or simply just object composition and method delegation blah blah blah.
Maybe their master plan is to free people from the broken OOAD baggage that comes with classical inheritance. Who knows?
But once you do that, there will be scapegoats:
- Classical inheritance = broken OOAD?
- ES6 class = broken OOAD?
- Anything with word ‘class’ or ‘inheritance’ = broken OOAD?
And there will be false positives, as demonstrated in the stamp example above:
- Factory function = good OOAD?
- Anything with word ‘composition,’ even though it technically means the same thing as inheritance = good OOAD?
But maybe, it’s good enough. Only time will tell, I guess.
You may be interested in this sequel: