9 ms·
Why composition is often better than inheritance
- millstone 12y agoConsider 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 could relate them all via an interface, instead of inheritance; now we can store IEntity or whatever. We will soon discover three needs that are awkward to address: 1. Whenever we want to add some new method (say `fall`), we must go back and implement it separately in each class. 2. Different classes will want to share implementations. For example, both Characters and Pickups bounce on fall. 3. Some classes will want to specialize an implementation to do more. Characters bounce on fall, but also take damage. In practice you may end up with both: an interface that your engine talks to, but also a common base class that provides sane defaults. So while interfaces allow uniform interactions with disparate classes, inheritance provides that and also the ability to share and specialize the implementations. So inheritance solves some problems that interfaces cannot. See also default methods in Java, which makes an interface more like a class, and implementing an interface more like inheritance. The documentation even says that a class that implements an interface inherits its default methods.
- ufmace 12y agoI'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.
- yeureka 12y agoIn my opinion the best arguments against inheritance were written by Richard Gabriel in his book Patterns of Software: http://dreamsongs.net/Files/PatternsOfSoftware.pdf http://dreamsongs.net/Files/PatternsOfSoftware.pdf
- chton 12y agoWhile 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 http://en.wikipedia.org/wiki/Composition_over_inheritance)
- Torn 12y agoThis 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 and mixins (if available in your language) go some way to helping.
- EdwardDiego 12y ago> 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.
- dtech 12y agoDo you have examples? Most modern Java libraries I know (e.g. Guava) implement interfaces and heavily use composition. The "inheritance based" things mostly got deprecated/replaced when Java 1.5 introduced generics and most libraries needed to be heavily changed anyways.
- Torn 12y agoI've seen a lot of it in product codebases (both Enterprise and Startup) I've had to work with. I guess it's different when you're maintaining a library - you have more freedom to version up and rewrite things.
- jiaweihli 12y agoI think this is a tooling issue. People initially tend to favor inheritance because it looks cleaner than composition. Mixing a lot of unrelated code in the same class makes things hard to find. (which method applies to which composed object?) In languages that build in a concept of traits/mixins however, this isn't an issue.
- k__ 12y agoI think so too. In JS it's rather easy to compose objects. In Java it feels clunky, you code looks kinda wrong when you done.
- zo1 12y agoI don't quite understand this. In my opinion JS "classes", if you want to call them that, look, feel and act very clunky. My opinion, of course... Please could you explain to me how you feel Java "classes" feel clunky and look wrong? I'm genuinely curious, and open to being convinced. Note I substituted your usage of "objects" with classes, because they're vastly different concepts. I know, murky water when it comes to JS, but still.
- k__ 12y agoMy point was, classes and inheritance feel natural in Java. Object-Composition feels natural in JS.
- falcolas 12y agoI disagree that mixing remove this issue. Even with mixins, you will still have places where all of that boilerplate from composition comes back, just with a concept like Python's `super` thrown back in. And mixins in the wild are rarely so pure as people like to think they are - they often inherit from their own parents or other mix-ins, creating the diamond (or worse!) inheritance problem outlined in the article.
- andybak 12y agoThe Wikipedia article chton mentions (https://en.wikipedia.org/wiki/Composition_over_inheritance https://en.wikipedia.org/wiki/Composition_over_inheritance) ends with the following when discussing the drawback of composition (boilerplate for forwarding methods): "This drawback can be avoided by using traits or mixins." Now this is where things get a little blurry for me. Mixins can help with the main drawback of Composition - but Mixins ARE inheritance - so isn't this a contradiction? If I use PhysicsObjectMixin in my CharacterComposition class then I have to inherit from it. So aren't we back with the perils of inheritance?
- chton 12y agothe idea is that you create a mixin that has only the reference to the physics object and the forwarding methods. If you implement that mixin, it will be exactly equivalent as writing them out in your class, but you are spared from having to do it for every single class with a physics object. Remember that a mixin, by definition, isn't the same as inheritance. The methods, fields and properties are compiled into the class, not inherited. It's essentially a fancy way to 'include' a code file.
- seanmcdirmid 12y agoMixins can be implemented in a variety of ways, they don't need to be unmodularly inlined into class/object definitions as in scala. Also, mixin-style inheritance is by definition linearized multiple inheritance (at least according to cook/bracha, things get weirder with the Gabriel/Common Lisp definition).
- chton 12y agoIt'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 on the Bracha-Cook paper about them.
- kissgyorgy 12y agoIn Python, we use mixins. Mixins can only inherit from 'object' and nothing else, like this: class PhysicsobjectMixin(object): def update_physics(self): pass def apply_konckback(self, force): pass def get_position(self): pass class FightMixin(object): def attack(self): pass def defend(self): pass class TalkMixin(object): def say_something(self): pass class Character(PhysicsobjectMixin, FightMixin, TalkMixin): pass class Pickup(PhysicsobjectMixin): pass class Projectile(PhysicsobjectMixin): pass it's still inheritance, but the classes will be flat; every class only inherits one deep, so there will be no diamond problems and no repeating code.
- teamhappy 12y agoIt's pretty much the same example I used here a couple of days ago: https://news.ycombinator.com/item?id=7976227 https://news.ycombinator.com/item?id=7976227 Game development seems to be the poster child for composition over inheritance. Here's a lengthy article that explains it way better (IMHO): http://gameprogrammingpatterns.com/component.html http://gameprogrammingpatterns.com/component.html
- userbinator 12y agoWhile 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 complexity only changes form, so instead of tracing the flow through an inheritance hierarchy you're just doing it through chains of forwarding methods. It's for this same reason I don't believe so much his argument for readability and short classes - breaking everything up does not make things simpler, it makes the complexity spread out over a larger area; while it may be true that it is easier to understand an individual piece, it becomes more difficult to understand the system as a whole. This is especially important when debugging, where "can't see the forest for the trees" is a big hindrance. I think his example of flexibility is the strongest argument for composition, because in that case the forwarding methods are not a waste - they would need to do (useful) work to determine which of the multiple composited objects they would need to work with. Being mostly a C programmer who does OO-things, I use inheritance when it's obvious that most of the "methods" will be passthroughs to the "superclass", and composition when there is something more that needs to be done. Also, as I am not constrained by the OO model/conventions of the language, it's more flexible in that I can do things like "inherit" multiple times and even change that at runtime, so there is really no strict separation between composition and inheritance; to me, it's just "which function do I set this to point to."
- twic 12y ago> The complexity only changes form, so instead of tracing the flow through an inheritance hierarchy you're just doing it through chains of forwarding methods. It's for this same reason I don't believe so much his argument for readability and short classes - breaking everything up does not make things simpler, it makes the complexity spread out over a larger area; while it may be true that it is easier to understand an individual piece, it becomes more difficult to understand the system as a whole. Preach it! Whilst immense monolithic classes are bad, smashing a system up into a million tiny bits is just as much of a barrier to understanding. It is baffling to me that this is not immediately obvious to everyone. See also the microservices movement!
- yayitswei 12y agoBy the way, I encourage everyone to try out their game, Awesomenauts. Think Super Smash Brothers meets Dota. It's a lot of fun to play!
- kilemensi 12y agoI think one of the biggest reasons why most libraries/frameworks/apps/etc. use inheritance over composition is the easy with which the underlying languages allow the use of inheritance as opposed to composition. Most of these software writers know SOLID and other OO principles but when they're faced with the actual implementation, inheritance is just too damned easy to implement.
- _pmf_ 12y agoThe burden of proof should fall onto the user of inheritance to justify his decision. Interfaces plug delegates is more tedious to implement, but greatly reduces the chances of an architecture turning into a complete train wreck. I often wonder why declarative delagating is not a first class concern in programming languages.
- fithisux 12y agoWhy often and not always? Can you provide an exceptional case?
- kstenerud 12y agoIn this case I'd argue that the roles are a bit messy and Character has too much knowledge. Character should not know that physics objects can be updated, and certainly shouldn't be calling updatePhysics. You could end up with an updated Character interacting with a Character whose physics state hasn't been updated yet. applyKnockback: Character -> Physics object -> Physics engine updatePhysics: Physics engine -> Physics object -> Character new position (x, y) updateCharacter: Character reacts to change
- javinpaul 12y agoCouldn't agree more than this. I have also shared my 2 cents on Why composition is better than Inheritance for Java Programmers here http://javarevisited.blogspot.sg/2013/06/why-favor-composition-over-inheritance-java-oops-design.html http://javarevisited.blogspot.sg/2013/06/why-favor-compositi...
- pllbnk 12y agoWhen I try to choose between the two, I often like to think if the object I try to inherit from is from the same domain/context and solves a related problem. In the example in the article a PhysicsObject solves the problem of calculating coordinates in space and from the beginning it was not designed as something to be used in the game by itself. While the character participates in the actual game and executes the game logic. The character does not 'inherit' from PhysicsObject, it merely knows that PhysicsObject represents it in the space.
- zak_mc_kracken 12y agoWe've known this since at least 1994, when the GoF book [1] famously said: "Favor object composition over class inheritance" [1] http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612 http://www.amazon.com/Design-Patterns-Elements-Reusable-Obje...
- drumdance 12y agoHa, that's one of the few things I remember from that book. (Not knocking the book, just my poor memory.) I remember it being a big "a ha!" for a project I was working on.
- known 12y agoSounds like https://en.m.wikipedia.org/wiki/Triarchy_%28theory%29 https://en.m.wikipedia.org/wiki/Triarchy_%28theory%29
- al2o3cr 12y agoMeh. 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 class 'Window' and a class 'Button', then combine them with multiple inheritance to get a 'WindowWithButton', then inherit from that and a 'Scrollbar' class to get 'WindowWithButtonAndScrollbar'. I can't imagine anybody thinking of that as a "good" pattern today, but remember it was the '90s. :) Nowadays, the basic statement has been dogmatized to the point where you get code like this: https://github.com/elm-city-craftworks/broken_record/blob/master/lib/broken_record/composable.rb https://github.com/elm-city-craftworks/broken_record/blob/ma... This code re-implements Ruby's built-in method lookup algorithm, but with per-instance objects and none of the optimizations available to the real thing. It basically remakes inheritance, slowly and poorly, using composition. The other one that makes me scratch my head: people who rail against inheritance, then suggest mixins as an alternative. At least in Ruby, the two are equivalent. Check the `ancestors` property on a class with mixins sometime if you don't believe me. TL;DR (too late) - use your damn brain to make decisions, not just parrot slogans.
- SEMW 12y ago> 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).
- joevandyk 12y agoYou can only inherit from one class in ruby. You can mixin multiple modules into a class. So they aren't entirely equivalent.
- cryptophile 12y agoIn simplified terms, an object is just a hashtable pointing to a parent hashtable. Now the question becomes: Is it better to embed another hashtable inside the hashtable ('has') or better to store its fields in the parent hashtable ('is')? My question is: Why would this question even be relevant?
- taeric 12y agoI would prefer this with "why a shallower abstraction pool is better than a deep one." I've seen some compositional concoctions that were just as terrible to deal with as inheritance based ones. I think I've even contributed/originated some.