I have recently been digging into composition as related to object oriented programming.
Can someone summarize why this is better than say inheritance?
I can possibly understand the value of composition where you need a large team to be able to constantly add and remove functionality to a code base with this without having to mess with APIs override methods, etc...
But is there a fundamental benefit of composition over inheritance in ever circumstance? (I've read a number of articles on this recently and I am not understanding the benefits vs the complications it adds)
One way I think of it is that inheritance is like trying to model a complete solution all at once, doing the design up front and in a top-down manner. Everything's deeply tied together and more difficult to change later when you realise the design was wrong and when you need to add more stuff. Often, making a change in one place is like pulling a brick from the bottom of a pile - everything else is affected. It's very inflexible.
Composition is a bit more like building a solution as you go from the bottom up - you solve the sub problems one at a time and then just combine these solutions together iteratively to solve the larger problem. When things change, it's quite easy to change these small solutions, throw some away, build new ones, and recombine them. It's a much more abstract, much more flexible way to piece together your overall solution.
There's several fine replies already. Another aspect to consider is that inheritance is tied up with polymorphism. If your B class inherits from an A class, then anywhere that an A is expected, a B may be supplied instead.
If another body of code entirely was written and tested using As, without knowing that Bs exist, it may not work if given a B, unless that B happened to perform exactly the same as an A in whatever (unknown, unknowable) ways matter to that other body of code.
An object's interface is its specification. But most objects are woefully underspecified, with types (if you're lucky) and perhaps a bit of prose explaining what it's supposed to do. Preconditions and postconditions are rare, laws and properties are rarer still. Languages that are able to test those things hold are even rarer.
My own recommendation is to inherit only from abstract parents, and if your language supports it, to make any concrete class final. If you need some behaviour to be overridable at time of use, apply the Strategy pattern. There's a cost to this: you need to implement Strategy, and you will not think of all cases where behaviour should be overridable. But the cost of random errors that crop up at run time is IMO likely to be higher.
One way to think of the composition versus inheritance debate is to view inheritance as a relation specification, while composition is how you implement (ie mixin=copy some props from A-to-B) the relation.
The problem is that languages such as Java mix up the specification and the actual implementation, as a result you end up with an inheritance model that is rigid and doesn't account for the messiness of the real word, in other words things in the real world are not often related in a neat parent/child way.
Granted, Java does provide interfaces which can be viewed as a way to specify composition even though that doesn't appear to be it's intended use.
Another reason is it can help make testing more simplified. Instead of having to use a library to perform mocking or stubbing of specific functions within the objects, you can pass in fake instances of objects that help exercise the test properly. So instead of trying to mess around with the internals of an inherited object structure, you just redefine the objects during testing to meet your needs.
In other words, objects are composed of their dependent objects. I think this is where the idea of dependency injection comes from (though I find dependency injection frameworks complicated often, that's another story).
Building abstractions around useful/loosely coupled ways of encapsulating dependencies, as mentioned elsewhere in this thread "bottom up", is part of an organic process that evolves into getting design right. A huge "lever" that you can lean on in this process is being able to mock the things that you want, while also being able to use the "real" versions of the things that you want. This allows your code to pretty much always be testable and flexible.
It takes some practice, but once you get it your code grows so much better.
> It takes some practice, but once you get it your code grows so much better.
It's true. I had a bit of a roundabout journey to getting this idea.
Studying CS, I learned a ton about implementation inheritance, and only a bit about composition - enough to sort of answer a question about it, but not really get the feeling. So overall I didn't use it much.
When I started working, I saw implementation inheritance used a ton and got the impression that it really made things way too complex. There was more to the complexity than just this of course (design patterns everywhere, convoluted abstractions, strange monolith tools, etc) but it didn't help.
Later I started learning about FP, where composition is just second nature, and frankly one of the main methods of abstraction you get. So learning how to compose things came from functions.
Over the past few months I've come back to more OOP, and I've been using composition a whole lot more. And it does make a huge difference, and I don't think I've used any sort of inheritance for my own code at all at this point.
It's the difference between a strict hierarchy and cross-cutting sets. Composition allows you to "talk about" a given category of which any set of objects can be members, independently of their membership in other categories. Whereas with inheritance, if a member of category B wants to reuse aspects of category A, all B's must strictly be A's.
First, one needs to distinguish between interface inheritance and implementation (class) inheritance. When one talks about composition vs inheritance, one usually means implementation inheritance.
- Inheritance is static. Composition can be dynamic.
- Composition avoids "fragile base class" problem.
- Modifying base class requires access to source code. Composition does not.
One example is if suddenly you would require to inherit from the same object multiple times. Example - if you have a "team" object, you might at first inherit it from a list/set (as a team could be a list of members). However, later down the line, you might also want to model backups for your team. You can't inherit from list/set again, so what are you going to do? It it much cleaner in general to avoid inheritance as much as possible and only modelling the data as internal, not publicly-accessible state (by composing smaller state objects). Inheritance on the other hand (in most languages), can make your data model more visible from the outside.
This isn't to say there aren't valid use cases for inheritance (eg. runtime polymorphism), though if something can be accomplished through composition, it's usually the better approach as it only modifies inner details of an object, and not the external interface (of which the inherited classes are part).
> But is there a fundamental benefit of composition over inheritance in ever circumstance?
I don't have much time now, but a fundamental difference is that inheritance happens at compile time (it's rare/nonexistent that you can change the class hierarchy at runtime) while composition happens at runtime – this means that with composition one can change the behavior of a class at runtime by inserting in a field a different implementation of an interface, for example.
Inheritance is a conceptually more complex issue that isn't just about code reuse and doing things slightly differently than a parent. Composition doesn't make such assumptions. It only assumes that the parts conform with some interface.
I am replying to my own post here as a general thank you to all the replies. This was really helpful. This was a rare circumstance that searching blogs and tech resources really didn't help me understand.
I actually have a concrete example that I've been working on this week. At work we have a java service that creates an Excel workbook with several tabs. When the original authors of the code wrote the service, the specific tabs it needed to generate were fixed, so they just wrote the logic for writing the tabs directly into the method that processes the data, e.g. this general pattern:
public Map<String, someDataHolder> inheritedFormatData () {
Map<String, someDataHolder> formattedData = new ArrayList<>();
for (Record record : records) {
formattedData.put('tabA', processTabA(record));
formattedData.put('tabB', processTabB(record));
formattedData.put('tabC', processTabC(record));
formattedData.put('tabD', processTabD(record));
}
}
And that works well enough, until there was a need for having variations. In that case they just subclassed this formatter class and provided an override for "proccessTabB" for example.
The problem is that this week, I've been tasked with adding a new tab, tabE. There's no "processTabE" method to override. I basically have a few unpalatable choices:
- Directly add something like "formattedData.put('tabE', processTabE(record));", and the method "processTabE", to the base class. The danger here is that I have to make sure I don't accidentally break some behavior in one of the dozens of child classes (there's more complex inheritance than I'm sharing in this simple example).
- Inherit, then add another method that loops through "records" again and only processes tabE. That's safer from the point of view of inheritance, but it's very wasteful to loop through all the records more than once (there could be as many as a million in my particular case).
- Copy-paste the base class code into a new class. That solves the above problems but, ugh.
If we define a "Tab" type, with method for storing a key, processing and storing records, and providing that processed data, we can create a list of as many of these tabs as we want, and define each tab's manner for processing the data. Adding a variation to the report generation class is now a matter of defining a different list of tabs and passing that in. No more inheritance woes, plus it nudges you in the direction of better encapsulation of your state (ie each tab is responsible for itself, rather than that logic living in some base super class). Hope this concrete example helps.
A monk asked Java master Kaimu:
What is the “single-responsibility principle”?
Said Kaimu:
That a class shall do exactly one thing,
and do it completely.
The monk asked:
How shall I decide this “one thing”?
Said Kaimu:
The Fisherman does not build ships,
or we would call him a Shipwright.
The monk asked:
Is there no room in your philosophy for interfaces?
What if my class can serve as a Fisherman,
and a Shipwright and Sailor besides?
Said Kaimu:
What would you name such a three-headed monster?
The monk replied:
Shísho, after my uncle. He lives by the sea and does all these things.
Said Kaimu:
I would give your class Shísho three instance variables:
a Fisherman, a Shipwright, and a Sailor.
Then Shísho may implement those interfaces by delegation.
The monk replied:
I speak of inheritance, yet you answer in composition.
All of my uncle is a Fisherman, not just his left foot.
What use are objects, if we do not faithfully model the world?
Said Kaimu:
If I paint a fine shark upon this page, will you say, “Fine shark!”
or will you complain that it is flat and does not eat you?
The monk asked:
But how are we to know when the flat shark is shark enough?
Or when our uncle should fish with his left foot?
Said Kaimu:
Learning how is our “single reponsibility”.
If your system doesn't have anything to gracefully handle inter-component communication without coding custom adapters and introducing new dependencies, all your "reusable" components will become a legacy mess in 2-5 years.
Can someone summarize why this is better than say inheritance?
I can possibly understand the value of composition where you need a large team to be able to constantly add and remove functionality to a code base with this without having to mess with APIs override methods, etc...
But is there a fundamental benefit of composition over inheritance in ever circumstance? (I've read a number of articles on this recently and I am not understanding the benefits vs the complications it adds)