
Inheritance versus Composition - chmaynard
https://lwn.net/SubscriberLink/787800/b7f5351b3a41421a/
======
macintux
One of the most memorable eye-opening moments in my (admittedly fractured)
education was when when my software engineering professor listed the 3
fundamental tenets of OO—inheritance, encapsulation, and polymorphism—and then
asked us which one was optional.

Ever since then I've been on an anti-inheritance kick, although for the last
several years it's polymorphed into an anti-OO kick.

Anyway, I suspect most readers here have heard the idea that inheritance
should be used in limited circumstances (or not at all), but I wonder what
proportion of college students get that message today.

~~~
lolptdr
With all this angst against OOP, what other alternatives should a programmer
consider or what patterns of hierarchical logic do people suggest?

~~~
barrkel
Hierarchy - a single ontology - is one of the problems with OO. Ontologies -
note the plural - are useful, but choosing just one ontology privileges some
axis of abstraction over others, but there are different lenses you can put on
things that may make you want to classify by different criteria.

For example, you might have a bunch of things, some of which: (a) can be
rendered in some display - could be console, HTML, print output, screen, don't
care; (b) can be serialized to a variety of media; (c) have some common
configuration, that interacts with some configuration management system; etc.

If you try and shoe-horn these commonalities into a hierarchy, you end up with
classes that advertise being able to do too much and need to have "not
implemented" error conditions, breaking the Liskov substitution principle.

Interfaces are a way of breaking out. They're additive, rather than
hierarchical, from the implementing class perspective.

The other big problem (a bigger problem IMO) with inheritance is coupling. I
don't think there's any closer coupling between two pieces of code in an OOP
system than inheritance. Changes in a base class can radically alter behaviour
in all descendants. Depending on the implementation of overriding, descendant
behaviour can be accidentally hijacked with no compile time or run time
warning merely by updating a dependency. The API by which base class and
descendant class interact is bidirectional, with the flavours of both library
(you call them) and framework (we'll call you), and intermediate internal
state when e.g. overridden protected methods are being called is usually
sorely undocumented and liable to change.

~~~
marcosdumay
> Interfaces are a way of breaking out. They're additive, rather than
> hierarchical, from the implementing class perspective.

Java (and .Net by copying) choose to make interfaces additive and superclasses
combinatorial. It's a design decision and in no way intrinsic. Other languages
have different choices.

~~~
barrkel
Oh, I'm aware. However additive superclasses are much much worse for OOP,
there's good reason languages beyond C++ haven't walked the same road.

------
kazinator
In this issue, proper understanding is muddled by "OOP" languages that have a
static type system which uses inheritance as a gatekeeper to polymorphism.

So that is to say, if you want to dynamically substitute objects of class D1
and D2 in the same places in the program, these classes have to inherit some
base B. Then D1 and D2 objects can work in places that require a B. For this
reason, sometimes the B is a dummy that contains no implementation at all. In
recognition of such dummy bases, some languages have "interfaces" and
"interface inheritance" to formalize the concept.

In true OOP, there is no such requirement for D1 and D2 to be a new kind of B.
Objects are substitutable into a situation if they simply respond to all the
required methods. Inheritance is then used optionally just as a trick for
implementation reuse. That is to say, we not only want D1 and D2 to be
substitutable, but since they are similar, we would like to have a large
fraction of their code in common, not to mention the instance data which that
code refers to.

The problem is that inheritance is like a software fork. There is this basic
implementation of something B, and then these derivations D1 and D2 are like
branches: they use the B code, but they replace some of it with their own
versions of functions. Those functions initially correctly mimic what their
counterparts in B are doing. But if B is subsequently maintained, that can
fall apart. B functions can be altered, while their counterparts in D1 and D2
remain the same, not incorporating the new logic. This kind of inheritance
situation works best when it's all maintained by one developer, or team, as a
cohesive whole. Or else, the base has to be very well designed for inheritance
and maintained very carefully.

Inheritance has issues that interact with mutability. The circle-ellipse
problem goes away if objects are immutable. In the Common Lisp object system
there is an analogy there in that numbers of type _rational_ are subclassed by
inheritance into _ratio_ and _integer_. An _integer_ isn't a new kind of
_rational_ ; it's a restricted kidn of one, just like a circle is a restricted
ellipse. Since we cannot mutate an _integer_ (it is what it is), inheritance
models things just fine here; it is quite suitable for partitioning a
mathematical set. In terms of substitutability, it is perefect: certainly
wherever rationals are allowed, we should be able to use an integer or a
ratio, just like where ellipses are allowed, we should be able to use a
circle. If we ask an integer what its denominator is, we get 1.

~~~
alasdair_
>In this issue, proper understanding is muddled by "OOP" languages that have a
static type system which uses inheritance as a gatekeeper to polymorphism.

I am trying to think of a commonly used language that exclusively uses
inheritance to implement polymorphism. (Unless you consider Java’s interfaces
to be inheritance when they are the language’s alternative to using
inheritance.)

~~~
TeMPOraL
> _(Unless you consider Java’s interfaces to be inheritance when they are the
> language’s alternative to using inheritance.)_

Having come to Java from C++, I always considered Java's interfaces to be a
syntactic sugar on top of inheritance. In C++, there is (or was, back then) no
meaningful difference between "interface" inheritance and "class inheritance"
\- and why would there be, if it boils down to stringing up vtables together?

------
userbinator
_There are, of course, disadvantages to composition. It requires more code
than inheritance, as we saw, Ortiz said. It is often more difficult to read
the code using composition than it is to read code using inheritance._

I think this is the main disadvantage, at least in languages like C++ and
Java; if you want to be dogmatic about the OOP-ism of encapsulation and that
stuff, all those "methods that just call another method on the composed sub-
object" create lots of bloat that a compiler may or may not be smart enough to
inline, but a human certainly would have to deal with --- and that adds
overhead when debugging and otherwise maintaining the code. Speaking as
someone who really hates such "useless" code, composition (especially
sometimes of the nested variety) can get annoying really really quickly
---needing to "thread the call" to the right place through many composed
objects just feels more like busywork than accomplishing anything functionally
useful. If you inherit, you automatically get the union of the methods (and
only when there's name collisions do you need to disambiguate), but inherit
too deeply or widely and it gets more difficult to find where the code is.

Of course, none of this is relevant at all to the code I normally write using
C, where there is no inheritance and everything is composition, and not being
an "OOP purist", I'm perfectly happy with BAR_dosomething(&foo.bar);

~~~
alasdair_
>needing to "thread the call" to the right place through many composed objects
just feels more like busywork than accomplishing anything functionally useful

Doing this is a pretty strong code smell. If you have chains of object
references that need to know about the internal structure of other objects,
perhaps think about the Law of Demeter?
[https://en.m.wikipedia.org/wiki/Law_of_Demeter](https://en.m.wikipedia.org/wiki/Law_of_Demeter)

~~~
userbinator
That's exactly the problem with being dogmatic about OO --- even the page you
linked to mentions it:

 _Although the LoD increases the adaptiveness of a software system, it may
result in having to write many wrapper methods to propagate calls to
components; in some cases, this can add noticeable time and space overhead_

Suppose foo() inside class X needs an extra parameter or similar, but that
parameter actually needs to come from A which contains an instance of B which
contains a C which contains a ... ... which contains the X. Instead of
changing only foo() and the b.c....foo() in A, being dogmatic means having to
change all the do-nothing wrapper methods on that call chain.

If anything, I'd say _wrappers_ are the real code smell. You can probably tell
I'm not a fan of OO; I've encountered the aforementioned situation countless
times, where the real purposeful change takes a tiny fraction of the time
compared to going through all that other do-nothing code and changing it too.
The mind-numbing depth of indirection and wasteful bureaucracy is
excruciating.

~~~
Vanderson
Can you give a brief example of the situation you are describing? Or a link to
an article on this?

I am really interested in improving my coding using functional programming,
composition, etc... but so far, I haven't been able to understand the benefits
in my current circumstances. (I've done a lot of reading already) I am hoping
I will run into one, so I can see clearly why it's worth the effort to
implement. (ie, implement something new when upgrading/replacing some old OOP
code...)

------
yawaramin
Eiffel's take on inheritance might be an answer to its 'perils':
[https://www.eiffel.com/values/design-by-
contract/introductio...](https://www.eiffel.com/values/design-by-
contract/introduction/)

Here's the crucial part: in Eiffel, methods can have preconditions and
postconditions, and overridden methods in subclasses must _honour_ those
conditions in specific ways:

> The principle of subcontracting follows from these observations: a redefined
> version of r may keep or weaken the precondition; it may keep or strengthen
> the postcondition. Strengthening the precondition, or weakening the
> postcondition, would be a case of “dishonest subcontracting” and could lead
> to disaster. The Eiffel language rules for assertion redefinition support
> the principle of subcontracting.

In a sense, languages which support inheritance might not actually be doing it
correctly if they don't support these rules of 'subcontracting'.

------
kstenerud
It would be nice if languages provided mechanisms to support composition
without the bureaucracy.

Why should we write a ton of boilerplate just to forward a message to a
contained object? Why can't we just have:

    
    
        type MyType {
            member containedObject SomeOtherType
            forwardTo(containedObject) MethodA, MethodB as SomeOtherName, MethodC
            method SomeNewMethod() {some implementation}
        }
    

Then the compiler can optimize the path to call the delegated object directly
rather than through a secondary call via this object, since it knows what our
intent is.

~~~
perlgeek
Perl 6 does, with a "handles" trait:

    
    
       class MyType {
           has SomeOtherType $.containedObject handles ('MethodA', MethodB => 'SomeOtherName', 'MethodC');
       }
    

Very useful when you need it.

[https://docs.perl6.org/language/typesystem#trait_handles](https://docs.perl6.org/language/typesystem#trait_handles)

~~~
fanf2
Similar in perl5 with Moose or Moo

    
    
      package MyClass;
    
      use Moo;
    
      has member => (
        is => ‘rw’,
        handles => [qw[
          methodA
          methodB
        ]]
      );

------
kraftman
Inheritance drives me nuts. I'm trying to read through a class and keep it all
in my head, then I see a call that isn't defined, so I have to look at the
base class. Now I have two classes I'm trying to keep in my head. But oh wait,
it inherits from something else. Then I finally find the implementation and
ive forgotten the context it was getting invoked in. I'd much rather just have
an explicit import providing code reuse.

~~~
hnick
An IDE would help with this, at least for a strongly typed language ('go to
definition'). For something like Perl, good luck!

My other complaint is when the base class is updated independently of the
child class. Suddenly your code is breaking because a new field is being set
in the original function but not your override. For that reason I tend to
inherit to extend only, as much as possible. I prefer to write a wrapper if
it's a neater solution.

~~~
crimsonalucard
Definitely need an IDE for inheritance and OO heavy projects. It's not an
apple and oranges thing, it's required.

------
fphilipe
Aaron Patterson (aka tenderlove), Ruby and Ruby on Rails contributor, has a
good post titled "YAGNI methods are killing me" where he points out the
problems when inheriting from builtin classes such as String or Hash.

[https://tenderlovemaking.com/2014/06/02/yagni-methods-are-
ki...](https://tenderlovemaking.com/2014/06/02/yagni-methods-are-killing-
me.html)

~~~
boffinism
Side note: I always feel pretty weirded out visiting a website called
tenderlovemaking.com. It feels gross and creepy and not something I want when
I'm doing my professional job in a professional environment.

Is it just me, or does anyone else feel like this tenderlove/tenderlovemaking
thing is a bit uncomfortable-making and maybe not ok?

~~~
zaphar
It's not just you. I also think the domain name is in poor taste.

~~~
mercer
It sorta suits the Ruby 'aesthetic', perhaps, but considering Ruby/Rails'
current age and image, it makes me think of the Buscemi meme ([https://i.kym-
cdn.com/entries/icons/original/000/018/666/fel...](https://i.kym-
cdn.com/entries/icons/original/000/018/666/fellowkids.jpg)).

I say that with a lot of affection for Ruby though!

------
bgschiller
Nice write-up! Coincidentally, I wrote on the same topic, also in python just
a few days ago [1]

I came to largely the same conclusions, but instead of forwardable, I wrote a
new library called superdelegate that is, IMO, more explicit [2]

1: [https://brianschiller.com/blog/2019/05/03/two-sorted-
lists](https://brianschiller.com/blog/2019/05/03/two-sorted-lists) 2:
[https://github.com/bgschiller/superdelegate](https://github.com/bgschiller/superdelegate)

~~~
btown
Alternately, if you want to just delegate _everything_ , you can subclass
wrapt.ObjectProxy
[https://wrapt.readthedocs.io/en/latest/wrappers.html#object-...](https://wrapt.readthedocs.io/en/latest/wrappers.html#object-
proxy)

Python is scary sometimes. If it looks like a duck and quacks like a duck and
isinstance(duck), it might be an alien masquerading as a duck using
metaclasses to accomplish these feats. But with this craziness comes the
ability to make a transparent runtime proxy to just about anything in Python,
and do so recursively e.g. to track all uses of an object hierarchy.

~~~
bgschiller
I think I'm confused. If you want to delegate _everything_, why not just
actually use inheritance? Or even the object itself. What's an example of when
you would want an ObjectProxy?

Definitely agree about metaclass magic being a blessing and a curse. That
superdelegate library was fun to write and uses a metaclass to make the API
pleasant.

~~~
btown
If you don't know what you're going to need to wrap (heck, it might be
something with methods implemented with native code), but you want to override
or intercept a certain subset of calls, then composition/delegation is really
the only thing you can do. It's significantly more stable than trying to find
the class of an arbitrary object at runtime, create a subclass of it, and try
to copy the state of the original object.

For instance, I've seen instrumentation libraries monkey-patch database
cursors with wrappers around those cursors, so the library can detect usage of
the cursor without changing its behavior.

Django itself has a LazyObject class, which operates similarly to an
ObjectProxy but only evaluates its wrapped initializer when one of its
properties is accessed:
[https://docs.djangoproject.com/en/2.2/_modules/django/utils/...](https://docs.djangoproject.com/en/2.2/_modules/django/utils/functional/)

And I'm using wrapt in a project (which I hope to open source) where, by
recursively wrapping the return value of every __getitem__ and __getattr__
with a properly-bookkept proxy object, you can see what subgraph of a
complicated object hierarchy was actually used by a function call (say, the
rendering of a Django or Jinja template). Possibilities of such a
transformation are endless!

------
harimau777
What advantages do proponents of inheritance say that it brings to the table?
As far as I can tell, the code reuse aspect can be replaced by composition,
substitutability can be replaced by subtyping, and encapsulation can be
replaced by immutability and functional purity.

On the other hand, the problem that I see with inheritance is that it ties
code reuse and subtyping together. I don't see any reason why the two should
necessarily be related.

~~~
remote_phone
The mental model of inheritance is easy to grasp. I think inheritance has its
place as long as things don’t get too crazy. For 90% of projects out there,
inheritance is a simple model to understand and can be used just fine. It’s
when “architects” get overzealous and try to force everything into a hierarchy
where things get bad.

------
norswap
This is missing the biggest difference between the two, at least according to
me.

In composition (and its on-steroid cousin, delegation), the component/delegate
can't "call back" into the aggregating object.

Of course, this additional power can be misused, but it's what should inform
the choice between inheritance and composition. Use the least powerful thing
that will work — so composition if you don't need to call back into the
aggregating object.

~~~
gugagore
Thanks for pointing this out.

I can't think offhand of an example to illustrate when you would like this
"call back" functionality in a way that you can't anticipate when you design
the component object. Do you have a killer-app for inheritance?

~~~
norswap
Any time the component needs to call a method or access data from the
aggregate. The kind of thing that happens all the time but the pattern is
abstract so there's no killer example.

e.g. the component manages some kind of list, and the content of this list has
to be kept in sync with some field in the aggregate.

No example is going to be perfect, because if you have _full_ control over
both classes, well then you can always change their interface in a way that it
isn't required anymore. One need to consider the reason the thing should be a
component in the first place (maybe the pattern is reused in multiple places)
and other external and/or historical constraints on the code.

------
codr7
Go makes composition less powerful than it could be by slicing structs, which
means a lot of hoop jumping to get template methods (where blanks are filled
in downstream).

I've been playing around with first class environments [0] lately in g-fu [1],
which allow convenient and flexible composition of data and behavior using
only what was already there.

I have a feeling Lua might be playing similar tricks with its tables, but I
lack enough experience to judge.

[0]
[https://github.com/codr7/g-fu/blob/master/v1/lib/eos.gf](https://github.com/codr7/g-fu/blob/master/v1/lib/eos.gf)

[1] [https://github.com/codr7/g-fu](https://github.com/codr7/g-fu)

~~~
akyu
>slicing structs

Can you explain what this means? Still learning Go. Google didn't help.

~~~
codr7
I'll try :)

The issue is that you can't interface methods from pointers to a contained
struct, since it has no idea where it is or how it got there. Instead a
pointer to the full struct needs to be threaded all the way down to where the
overridden function is called.

I sort of understand why, but this is a major missing piece that makes it very
frustrating to add anything but trivial default implementations to interfaces
in Go from my experience.

------
js2
This talk from PyCon 2013 covers much of the same ground: "The End Of Object
Inheritance & The Beginning Of A New Modularity"

[https://www.youtube.com/watch?v=3MNVP9-hglc](https://www.youtube.com/watch?v=3MNVP9-hglc)

------
shmerl
Modern programming languages like Rust avoid classic inheritance:

[https://doc.rust-lang.org/book/ch17-01-what-is-oo.html](https://doc.rust-
lang.org/book/ch17-01-what-is-oo.html)

------
Fannon
One article that helped me to understand the OO inheritance problems
understand much better is this one:

[https://www.sicpers.info/2018/03/why-inheritance-never-
made-...](https://www.sicpers.info/2018/03/why-inheritance-never-made-any-
sense/)

It argues that there are three different kinds of inheritance and in OO you
often mix them up into one hierachy - which may work in some situations, but
in some it just can't. Personally, I've experienced this kind of conflict
often enough.

------
slifin
Java esque OO is often tauted as a way to reuse code, but incredibly does the
opposite by dictating a tree design and then actively disallowing reuse
between arbitrary leaves of the tree

I think the worse part is this plague has descended into other languages, if
you want different behaviour for different things (but common in someway) just
accept a function into your function & invoke it, tada more efficient
dependency injection

------
gerbilly
I find that in a lot of critiques of programming methods, the proposed
solutions often contain the implicit assumption that the 'code' is a small to
medium codebase with a single developer.

If a sole developer is the only one working on it, of course any approach can
contain the complexity.

But, if in the language you are using, the alternative to inheritance is
composition via code duplication, then I think that's even worse than
inheritance, especially once the code is unleashed on a whole team of
maintenance developers.

Who says all that duplicated code will be left in its pristine state, and used
only for it's intended 'compositional' purpose? People might start hanging
special logic in there or whatever...

Generally, for projects with large teams like this, the less code the better,
because that means there's less code for them to screw up.¹

If that means using inheritance for reuse a bit even if it's not 'pure' OO or
whatever, I'd chose that over duplicating code all over the place.

1: I know Java isn't fashionable, but for many reasons, it's perfect for this
kind of project.

------
Keshavjois
Fun way of describing inheritance :D

"You wanted a banana but what you got was a gorilla holding the banana and the
entire jungle." -Joe Armstrong

------
primaryobjects
Composition also facilitates utilizing (GOF) design patterns. They can make
code much cleaner and reusable when used properly and sparingly.

------
mharroun
I guess I'm old school but I have had huge success with OOP and the long lived
design patterns.

Patterns like adapter, bridge, and strategy are emensly useful in archetecting
software that will last more then the next iteration or 6 months.

Perhaps I havent seen the right resources but so far function programming seem
weak to me compared to oop which has things like domain driven design, and a
very long list of battle tested design patterns.

------
chrisweekly
I'm partial to Eric Elliott's take[1] which highlights the brittle rigidity of
_classical_ inheritance in particular (vs eg prototypal inheritance). "OO" is
often conflated with class-based inheritance per se.

[https://link.medium.com/k0YynV2rxW](https://link.medium.com/k0YynV2rxW)

------
jzoch
Grpc-Java makes staying away from inheritance incredibly difficult (since they
dont present an interface to the service methods you want to call). This makes
adapters, decorators, and other useful patterns difficult to employ without
the rigamarole of defining the interface yourself and duplicating the
delegation.

------
ilitirit
I feel many people are overlooking the most important bit:

> He ended with a Spanish question that was evidently made popular in a US
> advertisement: "¿Por qué no los dos?", which means "Why not both?".
> Inheritance and composition are both useful tools; developers should
> understand when to use each.

~~~
zaphar
When should you use inheritance. I can think of one use where it's acceptable
and not dangerous. Implementing base class methods that are stateless helpers
for sub classes.

But I'm not sure that inheritance is the best way to solve it. You would
probably want to use interfaces and static methods instead. Basically I
literally can't think of a case that isn't better solved by another way.

Some languages may not give you those other options, in which case, by all
means use inheritance. Just make sure you are careful not to shoot yourself in
the foot while doing so.

------
dennisgorelik
The main problem that inheritance introduces -- is irrelevant references to
methods and fields.

If I want to see what code is using a method I am interested in, Visual Studio
shows me all related code. But if other developers overrode that method in
their own classes, then in addition to seeing ~5 references I like to see, I
have to sift through ~50 other references that are irrelevant to my
investigation.

So 5 minutes investigation turns into 55 minutes investigation. As you can
see, that "references spam" can easily slow down research and development 10x
or more.

------
fao_
This is amusing. Earlier today I was reading through Sonya Keene's book on
CLOS, and wondering why OOP had to be hierarchical rather than compositional,
and wondered if there had been any work done in this space.

------
akyu
Been hacking on a side project in Go, after coming from a career of nearly
exclusively using OO languages, with a little bit of functional here and
there. And of course enjoying all of the new functional features that nearly
every big language has been racing to implement these days.

At first there I had some quirks, along the lines of "how do I get thing X
from A to B", or "these things are similar how do I share code between them".

Everytime this happened I couldn't help but go through a process of solving
the problem in OO and then unwrapping it into a non-OO solution. Which doesn't
always turn out well.

Eventually, once I became comfortable with Go, I got to the point where I just
code things in the most obvious way possible. This struct has these fields.
This function takes x and produces y. I dont try to take a big picture view of
the software as a whole, Im just solving a bunch of small localized problems
one after the other. Then I refactor periodically to get rid of anything
completely stupid, and make sure the big picture looks ok. The name of the
language is quite fitting, you just go.

Whereas in OO, for me at least, it feels like im constantly bouncing all
around the abstraction layers of your program. Any change I make here in class
X could affect these other classes. Class A is dependent on Y, but so is class
B. How do I share it between them? Maybe dependency injection? Subclasses? Do
I refactor to an abstract class? Wait, should it be an abstract class or an
interface, what's the difference again? Is this generic enough? I only need to
use this in this one place, but I'll make it generic because "best practices".
What if I might need this other subclass eventually but it's not on the spec
right now?

I'm obviously exaggerating, but I think most of us have been there. I find I
often end up in a competition against myself about being more clever or more
reusable with my OO code.

Our job is already hard enough when you have to decide and reason about
algorithms, data structures, concurrency etc. The extra layer of code patterns
usually just obscures things.

I'm not a complete hater though. Using a well designed OO library can be an
absolute delight (PyTorch being a great example). But OO is the kind of thing
were its not just garbage in garbage out. The garbage multiplies and creates
even more garbage.

------
collyw
"You wanted a banana but what you got was a gorilla holding the banana and the
entire jungle."

Aren't mixins a way of avoiding this problem while using inheritance?

~~~
miiiiiike
Some languages don't support multiple inheritance so mixins aren't an option.

~~~
marcosdumay
And then we get programmers that only know those languages claiming that
inheritance if bad because the only language they know only supports an
useless simulacra of it.

------
thaumaturgy
I wonder why so many books and articles like this one are written as though
the approach they're championing is the only right approach, or always the
right approach for every situation. There's never any consideration like, "in
the following examples, this might be a good tactic..."

Looking at the subject for this post:

Inheritance is a good, straightforward approach when you have multiple related
classes which share a lot of code but don't interact directly with other
unrelated classes:

    
    
        class document
        {
            public function title ()
            {
            }
    
            public function get_plain_text ()
            {
            }
        }
    
        class word_document extends document
        {
            public function get_plain_text ()
            {
            }
        }
    

In a document management application, you might need to represent lots of
different kinds of document formats, each with their own specializations, but
also with a lot of common attributes too. These document classes might not
need to interact directly with a database or a network socket, so a
straightforward and traditional class hierarchy would be the natural choice
here.

But then let's say you wanted to build an abstract data pipe. A data pipe
needs to talk to lots of different things; maybe it's a plain old network
socket, maybe it's a database engine, maybe it's an API. Here, you get no
advantages from using inheritance, because although each data pipe is trying
to mimic the behaviors of any other data pipe, under the hood their code is
completely dissimilar. You'd end up having to rewrite so much of the
functionality that there'd be nothing really for a base class to actually do.

This is where composability and interfaces are the right approach:

    
    
        class phabricator_datapipe implements datapipe
        {
            private $_api_client;
    
            public function __construct ($api_client)
            {
                $this->_api_client = $api_client;
            }
    
            public function search ($parameters)
            {
                //  Build a search query out of $parameters specific to
                //  this API, then:
                $this->_api_client->search(...);
            }
        }
    

Each datapipe class strictly follows a common interface structure. Dependency
injection is used during instantiation to compose the runtime object, so the
responsibilities in the datapipe class are limited to translating the
application's business needs into the specific structures required by the API,
while the API class takes care of authentication and error handling.

There is room for both of these approaches in the same application, because
they solve entirely different problems. The inheritance of the documents
classes solves the problem of dealing with similar-but-different units of data
in a maintainable, extensible way. The composable interface of the datapipe
classes solves the problem of interacting with completely different external
components in similar ways.

This likewise is where the post falls over, IMO. It presents an argument for
composition, but is too lazy to provide any of the context that really makes
composition look like the right answer. Whether a Car object should be modeled
as a child of a Vehicle base class, or whether it should be more of a
standalone class with other objects injected into it, depends entirely on
whether the application just wants to say:

    
    
        Vehicle->GetTimeToDestination(...)
    

or whether the application instead needs to:

    
    
        Vehicle->StartYourEngine(...)
    

(Aside: I think a major failing of posts and books on programming is using
contrived, made-up examples, especially when talking about abstractions like
object-oriented programming. Those don't help anybody understand the
advantages and disadvantages of different approaches. I'm using some examples
from applications I've written.)

~~~
mobjack
Inheritance usually looks more natural when it comes to toy examples.

It the real world, a few edge cases can break down an elegant inheritance
hierarchy and composition is often the better solution.

------
bmn__
article with better code, comparing languages and more substance:
[http://radar.oreilly.com/2014/01/horizontal-reuse-an-
alterna...](http://radar.oreilly.com/2014/01/horizontal-reuse-an-alternative-
to-inheritance.html)

------
barking
Problems caused by inheritance is not one people maintaining legacy vb6
software have to worry about.

But in the day the main reason VB6 was called a toy language was because it
wasn't fully OO, it lacked inheritance.

And in his 1999 book Francesco Balena showed how this handicap could be worked
around, using delegation.

------
eithed
Isn't it dependancy injection rather than composition? I'd think JS old time
"inheritance" where methods are being copied from class to class is
composition.

------
jgalt212
Python, in a sense forces inheritance, because with composition there are too
many dots (i.e. objects get too nested)

------
jyosh
prefer composition for code reuse and inheritance for polymorphism

