Live data from Hacker News

Why composition is often better than inheritance

joostdevblog.blogspot.com

81–90 of 97 posts

Re: Why composition is often better than inheritance

#81
post #75
post #69

Earlier quoted context omitted.

"I was told about inheritance, but had to learn from other coworkers and experience that composition is much favored over inheritance." You weren't taught that, because it isn't true. Inheritance is appropriate sometimes, composition at other times. In particular, when a two objects are modeled by an ISA relationship, you should use inheritance. When they're modeled by HASA, you should use composition. A Dog is not a…

You're missing the most important cases - there are cases where it could go both ways in terms of ISA vs. HASA, in which case (the article says) you should use HASA. Just saying "use the tool appropriate to the job" dismisses the entire problem where in the real world , it isn't clear what that is.

I'm not responding to the article, I'm responding to the parent, who is saying that encapsulation is "much favored" over inheritance. It is not. At least, not amongst more experienced programmers.

Even in marginal cases, you can reasonably decide that a data model should use inheritance. There's no presumptive bias against it.

Re: Why composition is often better than inheritance

#82
post #44

Earlier quoted context omitted.

Scala's implementation is definitely an interesting and a valuable one. I'm not debating that inheritance-based mixins are useful, they definitely are. It's just that they aren't necessarily inheritance-based, and there are many implementations of mixins that aren't, so defining it as such is at best somewhat limited and at worst misleading. On a side note: scala's implementation, internally, is composition-based, si…

The type enhancement means that traits could replace classes completely in scala, which is kind of exciting. The problem with traits is that state must be inlined even if implementations are not (so they are a mixture of mixins and small talk style traits). I'm well versed in how they are implemented in scalac. What non-CL mixins are not inheritance based? Python?

Oh absolutely, the type enhancement is what makes it so interesting to use, but the point is that it's not necessary for it to be a mixin. It's a very nice extra.

I'm not sure about python, but Ruby completely inlines the mixin's code. It needs to do this because instance variables can be created anywhere, including from inside the mixin, but they still need to apply to the object of the actual class. This gives interesting cases for collisions, where 2 mixins create the same instance variable, but it's a decidedly non-inheritance way of doing it.

Re: Why composition is often better than inheritance

#83
post #43
post #31

Earlier quoted context omitted.

It's true that they can be implemented in the same way, but the idea remains the same. Seeing mixins as inheritance is a far narrower definition than held by most languages (or libraries) that implement them. Mixins don't put any requirements on the polymorphism of the object that implements them, which ordinary inheritance does. It's common to use the Flavors/Lisp defintion of mixins, but I'll make sure to read up o…

The Brache-Cook has only a simple view on Mixins in CLOS and Flavors. Actually the more interesting parts are not described. Instead they focus on problems they perceive, but which rarely play a role in practice.

See Gabriel's Incommensurability essay:

http://www.dreamsongs.com/Files/Incommensurability.pdf

Re: Why composition is often better than inheritance

#84
post #43

Earlier quoted context omitted.

The Brache-Cook has only a simple view on Mixins in CLOS and Flavors. Actually the more interesting parts are not described. Instead they focus on problems they perceive, but which rarely play a role in practice.

See Gabriel's Incommensurability essay: http://www.dreamsongs.com/Files/Incommensurability.pdf

Thanks, I'm forgetting this paper too often...

Re: Why composition is often better than inheritance

#86

While it's possible to overuse inheritance, I don't think replacing it with composition is all that much better, and in addition all those forwarding methods that do nothing more than call another (could they even be optimised out?) are a great example of code that would need to be written, consuming resources like programmer time, but otherwise serves no true useful purpose to the functionality of the software. The…

That's a fair point, although long chains of forwarded methods could be indicative of poor design - writing inheritance via composition in much the same way that one may write Lisp via Java. Granted, it's very easy to say that this is a bad idea, but that's part of the art of good OO design. :-)

I ran into this at work recently when refactoring an inheritance hierarchy. My initial instincts led me towards many forwarding calls, but after taking some time to reflect on the problem further, I found that many were no longer needed given the smaller scope of each class.

Re: Why composition is often better than inheritance

#87
post #61
post #41

Earlier quoted context omitted.

Many of us still learned about OO in school, where inheritance was all but beaten into us, and composition was not mentioned. And based on what I've seen in interviews, this is still a common model taught in school. Composition is in the process of winning as the default method of composing things together in OO (see, for instance, Go), but it has not won yet. And on the topic of Go, note how most common OO languages…

Composition always takes more boilerplate, because inheritance is noting more than composition with a bunch of default choices for a privileged component (single inheritance) or arbitrary sorting of components (multiple inheritance) made so the programmer doesn't have to write them (but also loses control).

Expanding on shanemhansen's point, where in Python subclassing is just a matter of mentioning the superclass in the right place in the class construct but composition requires substantial boilerplate, in Go, composition is just a matter of mentioning the composed element in the struct definition and inheritance requires a lot of manual boilerplate. It isn't technically impossible to implement something that is as much "inheritance" as it is in Perl, or especially as much "inheritance" as it is in Javascript, which I say with full knowledge of the object model in Javascript, but you will have to implement it yourself. (Courtesy of the lack of generics you won't even be able to factor out the boilerplate very well.) Whereas composing two things together is as easy as:

    type TwoComposedThings struct {
       ThingOne
       ThingTwo
    }
where ThingOne and ThingTwo are types. If ThingOne and ThingTwo do not have any conflicting method names, you're done. (If they do, you can actually still compile but when you try to use a conflicting name you'll have to manually disambiguate, and some subtleties could arise.)

Re: Why composition is often better than inheritance

#88
post #62

Meh. I think this phrase has been repeated until it has lost any connection with the original intent and turned into a generic "INHERITANCE BAD! COMPOSITION GOOD!" without much meaning attached to either word. I haven't found the original source, but I've always presumed the statement originally referred to some of the bizarro "inheritance-as-composition" stuff in the early C++ days: for instance, you might have a cl…

> the basic statement has been dogmatized to the point where you get code like this:

From the readme of that repo: "It is not suitable for any real purpose ... it may be a fun starting point for palying around with design strategies ... [the compositional design] is probably a bad idea for a number of reasons, but is worth investigating."

When someone explains at length that some code they've put up on github is experimental, probably-a-bad-idea, not-for-serious-use, just-playing-around-with-design-strategies code, deep-linking to it in order to hold it up as an example of 'bad things people are doing nowadays' seems a little uncharitable.

It certainly makes me think twice about putting my own just-for-fun code experiments up on github in the future (without a disclaimer at the top of every file, anyway).

Re: Why composition is often better than inheritance

#89

Consider how these things get stored. In the inheritance model, you might have a big quad tree or some other data structure of PhysicsObjects, and just run through and call updatePhysics() on all of them. In the composition model, we now have multiple classes (Character, Pickup, Projectile), each with an unrelated updatePhysics(). This means code duplication to call the relevant method on each separate class. We coul…

I've seen this problem in projects I've worked on recently. It's gotten me more interested in Ruby mixins as a solution, though I haven't worked on any really complex object structures in Ruby. The project in question is in C#, and you can sorta-almost do something like it by creating interfaces and putting the methods that should be shared as extension methods on it, but it feels very hacky.

Re: Why composition is often better than inheritance

#90
post #3
post #2

While it's a well-written article, it really seems like beating a dead horse. Composition over inheritance is a basic rule of OO programming, so much so that it has its own wikipedia page ( http://en.wikipedia.org/wiki/Composition_over_inheritance )

This horse still needs to be beaten. Messy inheritance still plagues product Java, C#, and, with the increasing proliferation of MVC frameworks, JS these days. Hierarchies usually start out small. But, when new features are added and scope creeps, they get deeper and more abstract and messier. Substitutability (i.e. the L in SOLID principles) does require more boiler-plate when using composition though. Interfaces an…

> Hierarchies usually start out small. But, when new features are added and scope creeps, they get deeper and more abstract and messier.

We have real issues with some complex class hierarchies that another team likes - they keep adding yet another abstract subclass of an abstract subclass to handle more cases, so you can end up following logic up and down multiple levels of classes when reading code, and missing one overridden statement can dramatically change the outcome.

Post reply on HN