There are lots of relatively coherent arguments already floating around the interweb around why composition is generally preferable to inheritance when it comes to code reuse; lots of people have strong feelings both ways.
I should point out, however, that many OO languages (like Java and C++) make inheritance much easier than composition, and as such inheritance seems more useful than it actually is, simply because it's all you've got. In languages that have better syntactic support for delegation and other compositional techniques, the usefulness of inheritance is significantly lessened. In other words, the usefulness of inheritance is more accidental (i.e. because you often don't have other good options) rather than inherent (i.e. because there simply aren't better options that are possible or available in other languages).
I strongly disagree that "code reuse" is a reason to use inheritance in C++/Java. Composition nicely solves that problem. In fact, I would argue that someone who believes code reuse is an appropriate reason to use inheritance does not understand OO programming as well as they believe.
Use composition for reuse and interfaces for polymorphism. These can do the same as inheritance and more, and they do it more cleanly. Reuse and polymorphism are orthogonal concerns that inheritance tries to fuse awkwardly together.
Try Go if you’d like a familiar language without the inheritance that you can pick up in an afternoon.
Polymorphism does not really depend on inheritance. Go is an obvious counter-example.
Also, it isn’t like OOP is one programming paradigm. It is several that make up one region of a landscape. And within each paradigm there is room for styles.
Finally, if X is better: then ask instead how you (yes, you) will make other people use, and like, X. People change languages because they find something that helps them achieve goals.
> Inheritance conflates code reuse ("avoid writing duplicate code") with polymorphism (allowing for multiple different instances to implement the same interface).
Note that languages like C++ allow for inheritance without polymorphism, i.e. pure implementation inheritance.
However, I also think that composition should be preferred whenever possible.
Inheritance conflates code reuse ("avoid writing duplicate code") with polymorphism (allowing for multiple different instances to implement the same interface). It also allows for trampolining method calls up and down a hierarchy (a method in a base class might call another method which might be overridden by another class in the hierarchy).
Outside of OOP, we use composition for reuse and interfaces for polymorphism, and we don't trampoline method calls up and down a hierarchy because it's (probably?) always a bad idea. When we really need reuse and polymorphism, we can use both composition and interfaces, since the two are correctly orthogonal.
To me, inheritance and polymorphism are two different things. Polymorphism is about different units implementing an interface or equivalent protocol and that rocks. Inheritance is, essentially, dumping a bunch of code into your new class, and most of the time just imposes constraints and breaks API boundaries for no good reason. After studying and doing OOP for about 5 years, I don't see the advantages of inheritance over composition. The only value I see is libraries exposing base classes that enforce behavior on user-written subclasses, stuff like React's Component or Java's HttpServlet. Seems to me we can have polymorphism without subclassing as long as the programming language has a half-decent type system.
So the question basically drills down to why one should use composition instead of inheritance. Most OOP languages only allow inheriting from a single class, and that alone is reason enough to prefer composition in most cases. It's one of the reasons I'm particularly fond of traits/modules.
Class inheritance in OOP seems to be largely derived from such contrived examples as "Duck extends Animal." It's basically a worthless concept and OOP would be better without it. Composition over inheritance leads to much better code.
I saw a lot more composition than inheritance in C++ and C#. I think inheritance and polymorphism have their place. Like anything, they can be misused.
I don't see how preferring composition over inheritance makes OOP less relevant. In fact, GoF even suggests using composition over inheritance in OOP. Nor do I see how immutability and lambda functions are mutually exclusive to OOP either. You can have all of these things and still reap plenty of benefits from OOP. The benefits of OO polymorphism and several decades worth of architectural design patterns are not irrelevant just because functional programming concepts exist. Both should be used advantageously and when appropriate.
I think the reason people prefer inheritance is because it gives them the illusion that their code is simpler. They don't see the huge interfaces implied by inheritance because they are implicit.
Composition, on the other hand, is explicit. When you use it, you notice the complexities. That gives you an incentive to simplify your design, which makes you write actually simpler code.
Late binding polymorphism and encapsulation are both very important concepts but there's no particular reason that your unit of inheritance should be your unit of polymorphism, often the boundaries should be drawn differently. And inheritance is just a way of achieving code re-use that tends to make suboptimal tradeoffs between flexibility and DRY.
There's a certain conceptual and implementation elegance to OoO so I understand why it became popular. But in practice all the new languages moving away from it are doing so for good reason.
Drifting from the topic, but I never understood this urgent need for so much inheritance when doing OO, especially when using languages that don't natively have it, like C and JavaScript.
I really like OO programming (i.e. I'm particularly not one of the functional-fundamentalist OO-haters here on HN), but as I've grown more experienced, I've started to use inheritance less and less, and composition more and more. This is trivially supported in C structs, and you don't need any fancy approach for it, plus you can do with way less void pointer casting, which means less runtime errors.
Inheritance is very useful for a small set of relatively complex problems, such as language parsing or UI frameworks or particle simulations. For many other problems, including most applications (as opposed to libraries), however, my experience is that you can do fine without. This holds especially these days, when many of said complex problems are solved in excellent freely available open source projects, in nearly any language.
All code reuse can be expressed with composition and delegation, bringing more flexibility, testability, cohesion, decoupling, etc, etc, etc.
reply