
Class Hierarchies? Don't Do That - johanbrook
http://raganwald.com/2014/03/31/class-hierarchies-dont-do-that.html
======
timr
_" the real world doesn’t work that way. It really doesn’t work that way. In
zoology, for example, we have penguins, birds that swim. And the bat, a mammal
that flies. And monotremes like the platypus, an animal that lays eggs but
nurses its young with milk."_

Former biologist here. Actually...most living things _do_ work that way. A
human ISA primate. Genetically. Functionally.

If you merely focus on _outward behavior_ , you get to the same place that
early biologists got to with what was called "morphological classification"
\-- you find the weird examples of convergent evolution (e.g. _" OMG egg-
laying mammal!"_), and you're tempted to throw out the whole classification
system, even though it _mostly_ works, and the errors are merely distracting
from the inherent truth of the idea (that we're all related by genetic
phylogeny; we _literally share implementation_ ).

Anyway, programmers, learn from biology: when you see these kinds of errors it
probably means that you're classifying things incorrectly, not that you should
stop classifying altogether.

~~~
RyanZAG
Thanks for the input - the more cross disciplinary knowledge flow the better.
I like your take away in particular. The author is basically saying 'All this
OOP stuff that most of the industry has been doing for 20 years? All wrong! Do
it my way.' Hearing that biology still uses directed non-cyclic graphs for
classification (eg, single inheritance) shows just how powerful single
inheritance really is and that we maybe shouldn't be so fast in throwing it
away because it doesn't match something.

I think sticking to the 'age old wisdom' of using direct inheritance for IS-A
relationships and composition for HAS-A is still the right way to go. It's
been tested, it works, and using HAS-A for everything makes for less
understandable code imo. Often you can fix an IS-A by simply refactoring your
graph with a better understanding of the problem space - as it turns out
biologists have done as well.

~~~
derefr
In my personal experience, I've never found a true IS-A relationship in my
code. I've found lots of _interfaces_ , though.

A lot of the time, you'll think you're defining a superclass when you start
writing things like "Square IS-A Shape; Rectangle IS-A Shape". But Shape will
turn out to not define any common behavior, just a category you want to
restrict your inputs and outputs to; and you'll want to be able to assert that
anything is, in addition to whatever else it is, a Shape. So shape is an
interface.

A lot of the time, you'll think you're defining a superclass when you start
writing things like "User IS-A Person; ProjectOwner IS-A User". But it'll turn
out that you want to keep People around even when they stop being Users, and
to keep Users around even when they stop being ProjectOwners. So you'll
rearrange things and find that you're now asserting "User IS-A Role;
ProjectOwner IS-A Role; Person HAS-MANY Roles." And Role turns out to be,
again, an interface.

The only example I can think of that _does_ fit single-inheritance is when
modelling objects that directly express a genealogy. For example,
GithubForkOfProjectA IS-A ProjectA, or Ubuntu IS-A Debian. But these aren't
typically things you'd express as _types_ ; they're just _instances_ (of,
respectively, GithubProject and LinuxDistribution.) Each instance HAS-A
clonal-parent-instance.

I guess there's one _possibly_ -practical use of inheritance which I've nearly
implemented myself: if you force your database schema migrations to always
follow the Open-Closed Principle, and you want to migrate the rows of a table
as you encounter them to avoid taking the DB offline, then you _could_ have
two separate models for a Foo table, FooV2 and FooV3, where "FooV3 IS-A
FooV2". Each row has a version column, and is materialized as the model
corresponding to that version. Your code that expected FooV2s would then be
satisfied if it was passed a FooV3.

Does anyone actually do this, though? I don't just mean "row-by-row
migrations", I mean the "with two models, one version inheriting from the
other" part. And, if so, what do you do when you make a change to a model that
_doesn 't_ obey the Open-Closed Principle: where FooV3s break the FooV2
contract?

~~~
lmm
The one case where languages without traditional OO inheritance are painful is
where you want to do "like this other thing, except for this one particular
thing that it does differently". And sure, maybe that's always bad design -
but it comes up a lot in real-world business requirements. For all the people
saying "you should use alternatives to traditional OO" I've never seen an
actual example of how to do this better - you can patch those instances at
runtime (urgh), you can create an object that implements the same interface
and delegates to an instance of the base type (much less readable in every
language I've seen, and effectively reimplementing inheritance without the
syntactic sugar).

I think the right solution is simply to have firmer constraints about the
relationships between parent and child classes - just like decoupling a
class's implementation from its interface, it should be possible to separate
out the interface it exposes to child classes as well. The one
library/framework I've seen that does this really effectively is Wicket - it
makes extensive use of final classes, final methods, and access modifiers to
ensure that when you need to extend Wicket classes you can do so via a well-
defined interface that won't break when you upgrade your Wicket dependency. It
works astonishingly well.

~~~
derefr
You're correct in that special-case "like this other thing, except for this
one particular thing that it does differently" objects happen all the time due
to business rules.

But the Decorator pattern is _not_ "inheritance without the syntactic sugar."
Decorated objects, unlike subclass instances, are allowed to break the
contract of the object they decorate: they don't have to claim to obey any of
its interfaces, they can hide its methods, they can answer with arbitrarily
different types to the same messages, etc.

If a language made defining decorators simple, I think it'd remove a lot of
what people think of as the use-case for inheritance. (I mean, you aren't
_supposed_ to use inheritance for Decorator-pattern use-cases--it will likely
break further and further as you try--but people will keep trying as long as
the first steps are so much easier than the alternative.)

~~~
lmm
> If a language made defining decorators simple, I think it'd remove a lot of
> what people think of as the use-case for inheritance. (I mean, you aren't
> supposed to use inheritance for Decorator-pattern use-cases--it will likely
> break further and further as you try--but people will keep trying as long as
> the first steps are so much easier than the alternative.)

I actually agree with this, and I'd be interested to hear of language efforts
in that direction. But until there's this easy way to do decorators, just
telling people "don't use inheritance" isn't going to work.

------
jdlshore
Okay, having read the article, I think it goes too far.

Inheritance hierarchies have their issues, and raganwald touches on them, but
there's a strawman argument here.

(Incidentally, raganwald, I've noticed this about all your OO articles. You
seem to have a bias against class-based design. It's causing your essays to be
less brilliant than they could be.)

Fundamentally, you can think of inheritance as a special case of composition.
It's _composition_ combined with _automatic delegation_.

In other words, if you have A with method foo() and B with method bar(), "A
extends B" is equivalent [1] to "A encapsulates an instance of B and exposes
'function bar() { return this._b.bar(); }'."

This is _very useful_ when you want polymorphism. Writing those delegators is
a pain in the butt.

More importantly, it tells us how to use inheritance safely. Only use
inheritance when 1) you want to automatically expose all superclass methods,
and 2) don't access superclass variables.

Now, JavaScript does have the specific problem that you can accidentally
overwrite your superclass's variables, and that's worth talking about. But I
think that saying "inheritance is bad" goes too far. The article would be
stronger if talked about _when inheritance is genuinely useful_ , _the
problems it causes_ , and _how to avoid them_.

 _Edit:_ In particular, I want to see more about polymorphism. Polymorphism is
OOP's secret superpower. _Edit 2:_ I'm not saying polymorphism requires
inheritance.

[1] Not quite equivalent.

~~~
SideburnsOfDoom
I think that part of the problem is that while JavaScript can _do_ OO
somewhat, it is not fundamentally an OO language, and if OO is the first tool
that you reach for, you are likely doing JavsScript poorly.

~~~
cwp
Javascript _is_ fundamentally an object-oriented language, it's just
prototype-based rather than class-based.

~~~
SideburnsOfDoom
It seems more like a functional language to me, but YMMV:
[http://stackoverflow.com/a/501053/5599](http://stackoverflow.com/a/501053/5599)

~~~
kazagistar
You seem to be under the impression that those are somehow mutually exclusive.

~~~
SideburnsOfDoom
I'm sorry if you though so. I am not under that impression.

However as mentioned elsewhere (
[https://news.ycombinator.com/item?id=7500280](https://news.ycombinator.com/item?id=7500280)
) if most of what you do to organise your code involves passing functions to
functions and very little of it involves creating class hierarchies or
prototype chains, it's a fair assessment that the language that you are using
is more functional than OO.

The language that you are using may be a subset of the whole language, but
with JavaScript that's given - you have to find the good parts or go mad
trying. I was wrong about JavaScript as a whole, but maybe less so about
JavaScript as it is successfully used.

------
hibikir
Inheritance is easily overused, but that doesn't mean that we should just
avoid it altogether.

The problem IMO is that we are stuck in a view that inheritance is really
about ontology, when what we really mean, and want, is code reuse. It's very
hard to make a 5 level deep ontology not break down. This is why we have this
whole 'prefer composition over inheritance' business.

But while we are stuck with that kind of mindset in Java (at least pre java
8), we miss the capability of using inheritance as a way of adding mixins.
There is much power in using mixins as a way to clarify composition, while we
still keep the inheritance tree very shallow.

That's one thing we get with judicious use of the Scala cake pattern, which
you could easily reproduce in javascript: Composition without really having to
write a bunch of useless boilerplate. There's a nice talk out there about it.
Cake Pattern: The Bakery from the Black Lagoon.

The trick, as with most other programming techniques, is to use it carefully.

~~~
sunir
"what we really mean, and want, is code reuse"

I'm not sure if you meant it this way, so I'll underscore this point. Code
reuse is truly the worst "reason" to use inheritance, and in fact an anti-
pattern.

Composition is the only proper way to reuse code because composition is
explicitly stating you are using the composed code.

~~~
parasubvert
You're taking it too far. One should prefer delegation/composition in most
cases, but if you're trying to direct users to a restricted reuse model
(inheritance), then it works fine and is often a much clearer approach than
"Joe's custom delegation model".

There are plenty of examples of this in practice - someone pointed out
NeXT/Apple's UIKit for example elsewhere in this thread (which also uses
composition where appropriate). The Java collections hierarchy is also a good
one (LinkedHashSet < HashSet < AbstractSet).

------
kenjackson
Good article, but I think the real message isn't to not use inheritance, but
to use with great care.

Inheritance gives you a great characteristic -- isa relationships. And this is
something you don't get with composition.

That said, the fragility with poorly constructed base classes is real. But a
succinct base class can be very valuable and useful, and not that brittle.
Just don't stuff cruft in it that is of questionable value. Ask yourself
what's the least you can put in the base class and still provide value.

And this is where interfaces are also useful. You can get the isa relationship
w/o much of the brittleness as there is no internal state associated with the
interface. But you are still creating a hierarchy (just not of classes, but of
interfaces).

It's a useful article, especially for those new to the idea. But the takeaway
should be to use care. Not to avoid at all costs.

~~~
neilk
[edited because apparently I can't read; kenjackson did mention interfaces]

I think it's still a bad idea - as you note, isa is available without
necessarily having inheritance; we know inheritance is dangerous, so why
include that?

In languages like C++, C# and Java, it has long been expert advice to avoid
inheritance where possible, and use interfaces. But the language and language
culture is leading everyone astray. Defining interfaces is usually harder or
stranger-looking, and is taught as an adjunct to inheritance.

Other languages get this more right. Haskell has what it calls typeclasses,
but they are more similar to interfaces in the aforementioned languages.

We seem to be making _exactly_ the same mistake in the ECMAScript standard,
except it's even worse; you can't define an interface even if you wanted to.

~~~
jkrems
What would "defining an interface" even mean in a non-type checked language?
I'd say: ECMAScript already has better interface support than Java has because
it supports implicit interfaces. In Java two classes can only be interface-
compatible if they have a shared dependency/are at least indirectly coupled.
In a language with duck typing and without static typing interfaces are
present implicitly.

~~~
neilk
You're correct that adding interfaces to JavaScript would also require adding
optional type declarations. But there are lots of languages based on
JavaScript, or which compile to JavaScript, which offer optional type
declarations. It seems to me they could have gone down this path.

I don't agree that it's better to have an "interface" which exists purely in
the head of every programmer working on the code. Languages can be doing more
for us, and it doesn't have to be the kind of verbosity that Java offers.

~~~
jkrems
Maybe that was a misunderstanding: I didn't mean to say that interfaces should
exist purely in the head of the programmer. I did say that implicit
interfaces/protocols (Go, Objective-C) are better than interfaces that need
shared dependencies (Java/pure virtuals in C++). If I had to order it I'd say:
duck typing better than explicit interfaces; implicit interfaces that are
backed by static type checks "better than" duck typing. The "better than" in
quotes for the latter because it really is more about how much static analysis
the language supports and it's more a matter of opinion.

But I'm relatively convinced that interfaces/protocols that require coupling
of implementations are clearly inferior to the ones that are implicit.

------
hifier
There are some contradictions here. For example the author the following
statement about encapsulation violations being permitted, but not followed as
best practice:

"JavaScript does not enforce private state, but it’s easy to write well-
encapsulated programs: simply avoid having one object directly manipulate
another object’s properties. Forty years after Smalltalk was invented, this is
a well-understood principle."

However, the author doesn't seem to really understand this as he makes the
case that access to private state and behavior of a "superclass" violates
encapsulation:

"In JavaScript (and other languages in the same family), classes and
subclasses share access to the object’s private properties. It is not possible
to change an implementation detail for Account without carefully checking
every single subclass and the code depending on those subclasses to see if our
internal, “private” change will break them."

Well, yes they do allow access, but that doesn't mean you have to use it! This
is considered bad practice when extending any class in other languages that
I'm familiar with (C++, Ruby). Please take some of your own advice.

I do agree that hierarchies do not fit the real world as well the contrived
examples from my first OOP classes and they should be used with extreme
caution. Let's not throw out the baby with the bath water, however.

------
kwamenum86
I used to find these types of articles useful. But now I see them more as
dogma. Great engineers don't struggle with things like JavaScript inheritance
because they understand best practices and trade offs. So in general, I find
it more useful to read about best practices and trade offs than "xyz
considered harmful" articles that don't present viable alternatives to xyz.

That said whenever I see something on raganwald.com I'll still read it :)

~~~
jonahx
[https://yourlogicalfallacyis.com/middle-
ground](https://yourlogicalfallacyis.com/middle-ground)

The alternative he presented is not to share private state between class and
superclass. This really isn't a "know the right tool for the right situation"
kind of thing -- it's almost never a good thing.

You could accomplish this alternative, among other ways, by:

1\. Using composition and delegation. 2\. Using mixins, if your language
supports them. 3\. Using inheritance, but depending only upon your
superclass's public interface.

------
cwp
The substance of the article is spot on, but I have to take issue with the
terminology it uses. The problem he's talking about isn't classes, it's
inheritance.

This is an important distinction. Javascript doesn't have classes, but it
_does have inheritance_. The problems raganwald points out with the Account
example come not from the organization of the code into pseudo-classes, but
from the fact that it uses Javascript's inheritance mechanism.

It would be perfectly possible to write a version of the example that
exhibited the "fragile prototype problem," and conversely, one can easily
write a version in Smalltalk that uses composition instead of inheritance and
thus has no fragile base classes.

------
mercurial
It's just as true in other languages. More often than not, programmers
thinking about code reuse attempt to solve it via inheritance, which is almost
always the wrong answer. With some persistence, you can end up with a nice
four or five level deep inheritance tree with 10 methods randomly overriden at
various levels in the tree. Good luck figuring out how the damn thing actually
works if you didn't write it.

~~~
dgcoffman
Okay, but that kind of polymorphism is actually a replacement for conditional
logic, e.g. switch on type antipattern.

People hate conditionals. People hate polymorphism.

Everybody is wrong about everything pretty much all of the time, it appears.

~~~
cmbaus
In JavaScript, like other duck typed languages, you have dynamic dispatch
without inheritance.

For example:

    
    
        var objA = {doIt:function(){console.log('hello from a')}};
        var objB = {doIt:function(){console.log('hello from b')}};
        
        var doItDynamically = function(doitObj) {
            doitObj.doIt();
        };
       
        doItDynamically(objA);
        doItDynamically(objB);
    

a and b do not share a common class (since classes do not exist in
JavaScript), but they implement the same interface. For this reason, they can
be used polymorphically, as if they shared a common base class or interface in
Java or C++.

------
nabla9
We want to use classes because we want to want to be able to invoke an
operation and have the exact behavior determined by the type of the object or
objects on which the operation was invoked.

Common Lisp philosophy of classes and OO fits the thicketness of the real
world better. Generic functions, multimethods, mixings etc. Just embracing the
notion that classes and encapsulation might be orthogonal issue opens up the
way to use class system better ways.

------
cmbaus
I am bit concerned that the standard committee is going to make the language
worse while trying to fix it.

Yes it is inconvenient to do traditional OO programming with JavaScript, but
I'm not convinced that is a bad thing. Encouraging subclassing, as pointed out
by the author, could actually be detrimental.

~~~
camus2

        I am bit concerned that the standard committee is going to make the language worse while trying to fix it.
    

AFAIK the goal is write what you mean and not "fake stuffs because the
language lacks structure".

Dont want to use any ES6 new syntax, dont use it. And yes , the committee
SHOULD meet developpers demands as much as possible. Javascript has no clear
paradigm,everything should be on the table.

~~~
cmbaus
To clarify, what concerns me is that by adding syntax that is similar to Java,
developers coming from other languages (namely Java), won't take the time to
understand the differences between the two languages and continue to write
applications as they would in Java.

Everyone has their biases, and my bias would be to embrace the dynamic and
functional aspects of the languages that separate it from Java, rather than
creating new syntax that pastes over the fact that the language is
fundamentally different.

~~~
peterashford
I'm a Java programmer and I think classes in JS would be awful. Tacking on the
ES6 behaviour will not make JS a sane OO language, it'll make JS a bunch of
new, inconsistent behaviours tacked onto the old, inconsistent behaviours.

FWIW I'm with you: I think that treating JS as a functional language is a
cleaner fit that crowbar-ing OOP into it.

~~~
V-2
You don't like TypeScript?

~~~
peterashford
I think it was a valid approach to the problem and I salute MS for it, but no
- I don't like it. We considered it at work and felt we were fighting JS's
natural tendencies too much.

Our decision is to deal with JS on it's own terms. Clojurescript would be
appealing if it didn't involve an AOT compiler (and one that's not trivial to
set up in Windows)

------
tmsh
Class hierarchies are easily fragile, easily do not model things correctly
over time.

The cruft, the lack of DRY as the mapping between what is being modeled and
what is represented in the code -- accumulates with class hierarchies for
sure.

But the idea of inheritance and tree structures _has_ been around and strong
for the past 50 years -- not because people are inherently lazy -- but because
it models something very primitive about programming: the evolutionary nature
of development and thinking.

This is extremely non-mathematical, but extremely non-trivial.

Tree structures (and by extension 'hierarchies') are perhaps the most
fundamental way to organize data. There is nothing more sophisticated than a
tree search -- it is the basis of all exponential / efficient access times and
all knowledge and organization of memory. It's why evolution proceeds in tree
structures. We organize data in our mind in tree structures. Life over
millennia organizes itself in tree structures.

I'm sad to report that the world will not quickly become clear and abstract
and orthogonal a la Haskell or other pure languages. Knowledge, life,
everything proceeds via evolution, not something a priori. The sooner one
_really_ accepts that, the sooner one has new ways to interface with this
reality more practically and effectively.

People sometimes make the point of use the right tool for the job - as a
reason for still using OOP, etc. I'd not say that -- but that the right tool
for the job where the _job_ is an evolving code base is actually something
object-oriented with inheritance. Except where the domain is very precise and
known ahead of time. Otherwise, the manner in which it models evolution is
actually quite useful (despite the fragility of inheritance and the quick
ability to spin out of any orthogonal clarity).

People who program purely in Clojure or Haskell put the burden of this
evolution in their own development as a programmer (there is no extension,
there is just clarity and then rewrite). That's ok. People who use Java or
whatever in the enterprise because it's easier for other people to get on
board with it -- put the burden in the code. But the cost of modeling a
problem that evolves goes somewhere.

~~~
thomasahle
Are you saying that `nature uses is-a hierarchies, hence it's good for source
too.' or that `source code that models real world concepts so it should follow
the same concepts.'?

~~~
tmsh
Good question, thanks. I think I was conflating both -- but I think both
apply.

The benefit of is-a hierarchies -- perhaps the only big benefit (to be weighed
against all the other alternatives available) -- is that it solves for making
the next 'adaptation' as simple as possible. It optimizes for the very next
extension step. It is slightly less conceptually complex to extend an object
than to composite a new feature.

It does that at the cost of complexity and brittleness. But in both the real
world and and in source maintenance this is occasionally useful.

Let's say I'm a startup practicing lean and experimentation as much as
possible. What I want is a framework that allows me to tweak things with as
few steps as possible. What I don't want is to refactor every time I want to
do an experiment.

Anyway, code I think generally should model our understanding of the problem
space and the real world concepts in play. Extension is not a bad way to begin
modeling that understanding.. Because growing the first steps (even though
they will clutter because there is nothing encouraging orthogonality in OOP
besides one's own sense of cleanliness) is often good for the growth of our
understanding.

------
_getify
Huge fan of this article. Makes some really great points! Wish I could share
and vote it up a thousand times. :)

I have been writing about exactly this topic, of why classes don't make sense
(specifically for JS), in my second title of the "You Don't Know JS" book
series, "this & Object Prototypes".

In particular, Chapter 6 makes the case for an alternate pattern to class-
orientation design, which I call OLOO (objects-linked-to-other-objects) style
code. OLOO implements the "behavior delegation" design pattern, and embraces
simply peer objects linked together, instead of the awkwardness of parent-
child abstract class semantics.

[https://github.com/getify/You-Dont-Know-
JS/blob/master/this%...](https://github.com/getify/You-Dont-Know-
JS/blob/master/this%20%26%20object%20prototypes/ch6.md)

~~~
tasty_freeze
Kyle, the figures (fig4.png, fig5.png) are missing from that page. Other pages
too. I'm one of your kickstarter contributors, but I was unable to read &
review some of the chapters because significant parts of the text refer to
missing diagrams.

~~~
_getify
They're coming. It's an artifact of working with a book publisher and the art
department and all that. Sorry for the confusion.

~~~
tasty_freeze
A rough, hand drawn sketch scanned to gif or png until the official artwork is
ready would be 100x better than a broken link.

~~~
_getify
OK, draft figures posted now.

[https://github.com/getify/You-Dont-Know-
JS/tree/master/this%...](https://github.com/getify/You-Dont-Know-
JS/tree/master/this%20%26%20object%20prototypes)

------
V-2
"Only, the real world doesn’t work that way. It really doesn’t work that way.
In morphology, for example, we have penguins, birds that swim. And the bat, a
mammal that flies."

Yes but we have Single Responsibility Principle and while an animal is one
object in physical world, it doesn't mean it should be a single object in OOP.
Start breaking it down...

public interface BodyType {}

public class TwoArmsTwoLegs implements BodyType {}

public class FourLegs implements BodyType {}

public interface Locomotion<B extends BodyType> { void walk(B body); }

public class BipedWalk implements Locomotion<TwoArmsTwoLegs> { public void
walk(TwoArmsTwoLegs body) {} }

public class Slither implements Locomotion<NoLimbs> { public void walk(NoLimbs
body) {} }

public class Animal { BodyType body; Locomotion locomotion; }

Animal human = new Animal(new TwoArmsTwoLegs(), new BipedWalk());

(Code sample from an article in Software Development Journal by Łukasz Baran)

------
aturek
tl;dr: Superclasses make your code brittle.
[https://en.wikipedia.org/wiki/Fragile_base_class](https://en.wikipedia.org/wiki/Fragile_base_class)

How many people still try to fit their software design into the class
hierarchy model? I've been on the composition-not-inheritance side for so long
I can't do "traditional OO" justice, but I'd love to hear the counter-
arguments in case I'm wrong.

~~~
jdlshore
No, you're right. "Favor composition over inheritance" has been the go-to OOP
design advice for years.

The distinguishing feature of OO is _modeling concepts_ by _encapsulating
state and behavior_. For some reason, introductions tend to focus on complex
inheritance structures instead. I think that's really unfortunate because it
sends entirely the wrong message about OOP.

That said, I do occasionally use inheritance for polymorphism and limited code
reuse (such as [1]), but I keep it limited in scope, and I don't think I've
ever used a multi-level inheritance hierarchy.

[1] In my Let's Play TDD screencast series, I use inheritance when modeling a
"Dollars" value object. There's a Dollars base class and then various
specializations: ValidDollars, InvalidDollars, and UserEnteredDollars.

Source code:
[https://github.com/jamesshore/lets_play_tdd/tree/master/src/...](https://github.com/jamesshore/lets_play_tdd/tree/master/src/com/jamesshore/finances/values)

The screencast: [http://www.jamesshore.com/Blog/Lets-
Play/](http://www.jamesshore.com/Blog/Lets-Play/)

------
dclowd9901
Ever since I've started writing Objective-C, a lot of Javascript's glaring
issues have started to melt away. Yes, JS doesn't have an understanding of
protocol, but that's just a matter of implementing convention and being
disciplined. JS is remarkable in that it is so malleable that one can use
myriad patterns with it.

I've found that the delegate pattern, for instance, can be incredibly powerful
when used in conjunction with JS, especially when it comes to extending
classes functionality that may not be inherent to its topology (think class
Ant, to describe an Ant, and class FlyingAnimal -- wings, etc. --, to describe
a queen ant).

------
benrhughes
Although I'm very, very sympathetic to the idea that class hierarchies often
cause more pain than they're worth, using JS to make that point is a bad idea.

In most actual OO languages, there _is_ a clearly defined interface between a
base class and it's inheritors: eg in c# you have protected (to expose state),
abstract to force implementation and virtual to optionally allow
implementation. In no way are you forced to expose all internal state (even
though people often do).

I think there's a case to be made against class hierarchies, and also against
using OO in javascript. But I'm not sure Ragan made either of them well here.

------
einhverfr
What is missing from the discussion here is that where domain knowledge is
less important. For example, almost every widget system I have ever looked at
uses class inheritance because it makes it relatively easy to manage
consistent interfaces across classes. This is true of GTK, wxwidgets, and many
more.

It is true that the natural world doesn't necessarily admit of perfect neat
classifications generally, much less trees. However, when we are talking about
purely engineered solutions, the same arguments don't apply in the same way.

Here's an example. In LedgerSMB 1.4, we use (shallow) class inheritance in a
reporting framework. It works, and works well. Reports inherit an abstract
class with lots of hooks for customization but a lot of defaults.

In future versions we will likely be moving away from an inheritance-based
approach, not because of the arguments here or the maintenance issues (which
will crop up any time you rely on external components) but because we think we
can create clearer code by moving from a class/inheritance approach to a DSL
approach.

I am not sure that class contracts and DSL syntax contracts are necessarily
any different from a maintenance perspective other than the fact that the
latter strikes me as resulting in clearer code.

------
al2o3cr
Anybody have an original source for "prefer composition over inheritance"?
I've heard it for years, but never with an attribution.

FWIW, I noticed that in my old C++ book (Stroustrup '91) there's some _really_
bogus inheritance examples - window -> window_w_banner -> window_w_menu ->
window_w_banner_and_menu etc etc etc.

~~~
djur
The idea is presumably older, but I think the current usage can almost always
be traced back to _Design Patterns_ (GoF, 1995). It's in the Introduction.

I don't think Stroustrup really gets OO. I have yet to read a book of his that
doesn't have some absolute howlers.

------
monokrome
"Hey, guys. Here's a really poorly considered hierarchy of classes, where I
haven't really made any real effort to separate concerns or otherwise prepare
for the problems which I am specifically creating.

Now, look at all these bad things that I've done! NEVER DO ANYTHING SIMILAR
EVER."

That pretty much sums it up.

------
V-2
Yet another post titled "don't do X", which actually reads as "do not
exaggerate with X" or "did you know? X has some caveats".

Of course inheritance is not a solution for everything. Avoid using deep
inheritance hierarchies, you'll paint yourself into a corner. It's advisable
to prefer composition over inheritance. You should not be that guy who only
has a hammer and everything looks like a nail to him. But it doesn't translate
into: "hammer? don't do that!"

All the examples he's giving either demonstrate abusing the inheritance
concept or just show that it has certain limitations. There are well known
solutions and guidelines for dealing with problems such as fragile base class,
other than throwing the whole paradigm out of the window

------
DrJokepu
It's difficult to argue with this article. I can count on one hand the number
of times non-trivial class hierarchies (that is, more more than 2-3 classes)
made my life easer and every time that was the case I writing a compiler or a
code generator or something similar.

~~~
mpweiher
The Magnitude/Number hierarchy in Smalltalk is amazing.

------
porker
> It turns out that our knowledge of the behaviour of non-trivial domains
> (like zoology or banking) does not classify into a nice tree, it forms a
> directed acyclic graph. Or if we are to stay in the metaphor, it’s a
> thicket.

That completely chimes with my experience. I wondered if I was doing OOP
wrong, as any time the size/complexity of a project (or module) gets above a
certain level, a Thicket results in my code.

> Classes are the wrong semantic model, and the wisdom of fifty years of
> experience with them is that there are better ways to compose programs.

Where's your source/links? What? This needs expanding - if there are better
ways, outline your evidence and show us where we're going wrong :)

~~~
mamcx
That was sometime I want to ask too.

Probably is the go OO-model?

------
skybrian
This is generally true. On the other hand, interfaces are a _good_ thing,
especially in a language like Go where you can explicitly declare them and
then pass in anything that happens to match that interface. (In JavaScript,
this is unfortunately implicit, so it requires better documentation and
sometimes runtime checks to ensure sanity.)

If you implement an interface using delegation, you get something quite like
subclassing, except that the subclass-superclass interface is explicit and the
superclass's type is entirely hidden. Sadly, few languages make this easy so
it often requires some boilerplate or meta-programming.

~~~
cromwellian
Delegation can have a performance cost that inheritance doesn't have however,
at least, depending on the compiler. Go's current compiler seems engineered
for correctness, not performance, at least last time I checked, the JVM was
beating it on several benchmarks.

~~~
skybrian
Go seems to be headed towards pretty fast and predictable performance rather
than the fastest possible performance at the expense of predictability. (For
example, the work to replace segmented stacks with contiguous stacks improves
performance in some cases but also makes function call performance more
predictable.)

A JVM can have fast or slow performance depending on JVM flags, warmup time,
and the phase of the moon.

~~~
cromwellian
Not really bashing Go, but I was was disappointed that for what is purportedly
a 'systems programming language', performance was far behind C/C++/D/Mars/etc.
I expect a little more from a statically ahead-of-time compiled language.

For 80% of apps, that's just fine, but would you write a mobile web browser in
Go, or say, Call of Duty? Seems you'd be leaving 50% performance on the table
in some scenarios.

I tend to give 'systems programming language' special status. They're how you
achieve maximum system performance if not writing in assembly. You write
kernels, device drivers, virtual machines, and games in them. If they are
imposing severe performance or memory overhead, then they are application
programming languages, not systems programming languages.

~~~
dangoor
I think the messaging around Go has changed a bit since it was originally
introduced. If you look at the front page of golang.org, it doesn't talk about
Go as a systems programming language. I do remember the way it was pitched
initially, but I agree with what you're saying and think that even the Go
people themselves wouldn't lump Go in with C for things like kernels or
leading edge games.

------
malandrew
I like these articles, but I think there are more than enough of them out
there and not enough of those that show you how to use composition.

Everyone who has spent enough time with OO and classical inheritance knows the
problems, but most have never seen how to convert a mess of classes and
inheritance into a more functional approach with composition.

Don't get me wrong, there was good content here, but I was hoping that after
the conclusion, the post was going to go into how to use composition as an
alternative.

------
foobarz24
This reminds me of a design question I recently faced when building a simple
multi-player game. The idea was pretty standard; The server sent events
(player dies, player moves to pos, etc.) to its clients over a TCP socket.
When programming the client in Java (for Android) I wanted a clean way to
update the world based on the event type. In Haskell I would have done
something like

data Event = PlayerDied Player Reason | PlayerMove Player Coords | ...

and use pattern matching on the event type. In C I would have used a
combination of unions and structs with an event type. But how to do that in
Java? I ended up with

interface Event { public update(Game g); }

and used e.g.

class DeathEvent implements Event {

    
    
      DeathEvent(String player, String reason) { ... }
    
      update(Game g) {
        g.killPlayer(player, reason);
      }

}

Combined with a parsing function (public Event parse(String line) {...}) I
could read from the socket and update the game in a convenient way, but to be
fair I used that mostly because Java guys discourage you to use instanceof
although it seemed clearer.

So is this the preferred way to do something like this? I think "they" (the
OOP warriors) call this the Visitor pattern. However I really find the data-
type encapsulation in Haskell and other languages (in Python a tuple (type,
object) would do) superior. But maybe my Java just got rusty.

------
gboudrias
In javascript*

I think this highlights a problem with javascript more than OOP: We're trying
to fit it into uses cases that are simply too complex for its design. It
wasn't made to build your goddamn bank accounting system, it was made so that
"nonprofessional programmers" could animate things on websites.

But it also highlights a problem it doesn't take about: Language in computer
science and how it affects how we think about things.

In this instance, public and private are terrible names. They had to tell us
in programming classes that they're not related to security, which means that
the privacy metaphor is a terrible idea because it's not instinctual.

This in turn causes us to shoehorn class design into things they perhaps
shouldn't be. At this point, for complex programs we should be describing
things in a much more complex way than "accessible from the outside or not".
As the article points out, it doesn't _matter_ that the classes are external,
because you can just as well break things from the inside.

This is a complex problem, but I think the beginning of a solution is to a)
Depend on meta-information (or better implement a flexible, non-arbitrary
"access" structure) and b) Use the right tool for the right job, in this case
not JS.

------
protez
Class hierarchies are just mental tools, not how machines/programs/automatas
actually work. If the mental tools implode due to exceptions and complexities,
that's the problem of their uses, not tools by themselves. Before blaming
hierarchies, you should blame yourself for using tools in wrong ways.

------
mcv
Isn't he mostly complaining about lack of encapsulation? So what if you do
encapsulate your data? That's totally possible in most programming languages,
and it's even possible in javascript if you drop prototype inheritance. You
can use closures to encapsulate your data.

------
platz
This is interesting, reading some of the linked documents, my naive
generalization is that it pushes the entities into using things like maps and
dictionaries instead of static properties. The "system manager" stuff just
pulls things out of the maps and feeds them to functions to do work, so it is
very much 'data-driven' and I must assume more work is put into
"configuration" of the entities, just like a data-driven business process
requires "configuration" of the order processing pipeline.
[http://www.richardlord.net/blog/what-is-an-entity-
framework](http://www.richardlord.net/blog/what-is-an-entity-framework)

~~~
platz
Wonder if this implies you know what you are going to be doing in the future.
As said by Sandi Metz, the true value good design is to reduce the risks of
future change. I think it is a valid endeavor to study what reduces risk and
what increases risk. This doesn't have to be about "design patterns".

------
dangoor
Something I haven't seen mentioned in this discussion so far is the Data,
Context, Interaction (DCI) pattern.

[https://en.wikipedia.org/wiki/Data,_Context,_and_Interaction](https://en.wikipedia.org/wiki/Data,_Context,_and_Interaction)

As mentioned at points in this thread, some objects need different behavior
based on their context. DCI is a thought provoking way to represent that
(though one that not in common use and that is often described as "being done
wrong" on the object composition mailing list[1]).

[1]: [https://groups.google.com/forum/#!forum/object-
composition](https://groups.google.com/forum/#!forum/object-composition)

------
epx
Cocoa is good because, among other things, is treads lightly on class
hiearchies.

------
sktrdie
Personally I believe that software design is a highly subjective art. People
that are meant to maintain a piece of code are different. Some may be more
creative and less pragmatic, others may be more structured and clean in their
design.

The truth is that there's no silver bullet. You can build software based on
hierarchy of classes because your mind works better with that structure, but
others may find it completely inappropriate. We're human and our mind works
differently from one another.

In that sense I truly believe software is much closer to art than engineering.

------
tristan_juricek
This approach to discussing inheritance treats JavaScript as if you're a Java
or C++ programmer, and completely lacks any clarity with how inheritance in
JavaScript really works.

Best intro I've read on the topic I'm talking about is from Alex Sexton (just
read the last example, it really hits the nail on the head):

[https://alexsexton.com/blog/2013/04/understanding-
javascript...](https://alexsexton.com/blog/2013/04/understanding-javascript-
inheritance/)

So you _might_ want a wee bit of hierarchy, if you're thinking like those
"shared options" scenario, but not in the "OO type abstraction tree".

So, you might have something like a "Account.prototype.primeInterestRate"
property that you can change in a running program, and then all the other
types of account can calculate interest based on that shared property.

However, the more experienced jS developers I've met might take those
"Account.prototype.balance" and "Account.prototype.deposit" methods, and push
those into a "mixin" type (like "CurrentBalance") where those methods are
__copied __(not inherited) onto the child class prototype, and those methods,
might have initializer helpers to set up the "currentBalance" property they
use. This mixin approach only gets gnarly if there's any feature envy.
(Document your object properties clearly, folks. This is where javaScript's
flexibility often becomes a crutch - lots of issues happen if mixin code uses
"this.foo" for different things.)

Anyhow, what's interesting here is that Account carries the property that's
shared, but CurrentBalance carries "behaviors", and is _not shared_ , and your
"child classes" like VisaDebitAccount embed both in different ways. It is a
very different way of thinking about object relationships, and often works
smoothly.

But if you're using classes in JavaScript like you would Java, well, then,
you're not really using JavaScript, right? And, this whole talk about
biological-style ontology just becomes the wrong metaphor, because while
"humans are a primate" we can't change aspects of primates to add behavior to
people!

------
iamwil
That inheritance doesn't get encapsulated was the same way I felt about
modules in ruby. You could include them into a class or other modules, but the
interface was never well defined, and you could cause incompatibilities by
relying on the implementation of the class you include into, unless you're
disciplined enough to only use methods, and not attributes.

On the other hand, defining all the interfaces all the time, like in java was
painstaking. I hope for some sort of middle ground.

------
buzzybee
The most challenging aspect of inheritance modelling is to recognize it on its
intrinsics - the deliberate creation of coupled properties and behaviors
associated with the "isa" declaration - and not simply as a form of taxonomy.

Programmers are still inclined to use hierarchies. They are implicit to source
code in a more general sense, with indentation, nested logic, etc. But we also
need affordances to break up hierarchies and keep them limited.

------
menzoic

      ChequingAccount.prototype.process = function (cheque) {
        this.setBalance(this.getBalance() - cheque.amount());
        return this;
      }
    

...

------
EGreg
From a practical point of view, mixing in behavior and implementing via
encapsulation is usually easier to maintain and extend than inheritance.

------
graycat
Another battle in the 60 year language wars based on criteria that are so
obscure that have battles just over the darned criteria.

------
twfarland
My js style began with a heavy usage of classes. I then left that for
prototype composition. Recently, I've arrived at a style influenced by
Haskell's separation of functions and data - namespaced objects of functions
that act on plain json-serializable data. Flexible, simple, and perfomant. I
don't miss 'this' at all.

------
golergka
> That kind of ontology is useful for writing requirements, use cases, tests,
> and so on. But that doesn’t mean that it’s useful for writing code the code
> that implements bank accounts.

Isn't good, easy readable code look very similar to requirements it was
written upon?

------
sgy
It's pretty much a promotion/defense for SmallTalk. Why everybody has grudges
against JavaScript?

------
ajuc
Class hierarchies are OK when they have one level. It's essentially object
oriented switch statatement with bells and whistles.

I can't remember one class hierarchy more than one level deep that was worth
it.

They save a little code - true, but at the cost of coupling, making it harder
to change, and forcing early debatable decisions on programmer (which
classification is more important and goes first for example).

------
SixSigma
"Object Oriented design is the Roman Numerals of Computing" \- Rob Pike

For more quotes see :

[http://harmful.cat-v.org/software/OO_programming/](http://harmful.cat-v.org/software/OO_programming/)

Another, seeing as it's PG :

"The phrase 'object-oriented' means a lot of things. Half are obvious, and the
other half are mistakes." — Paul Graham

~~~
ternaryoperator
While I admire both Pike and Graham, I've never liked the use of zingers to
articulate a programming point. Invariably, programming involves trade-offs,
acceptance of limitations in one dimension to gain benefits in another. So,
zinging the limitations is easy to do and doesn't advance understanding.

------
bayesianhorse
In idiomatic Python, the only reason for class inheritance is for code reuse.

In other languages, especially Java, class hierarchies are a matter of self-
esteem.

Javascript programmers could do worse than emulate Python in this regard.

------
ssmoot
Ow

------
vermooten
Please can yo make the type in your blog even less readable? I think knocking
back the grey so that it matches the background should do it - you're almost
there just needs a tad more.

~~~
dang
This sort of sarcastic sniping is one thing we really don't want on Hacker
News. We don't ban people for it, but it should be downvoted.

I don't mean to pick on vermooten here; lots of HNers post comments like this.
Please don't post comments like this. Re-read what you post and, if it
contains sarcastic sniping, edit it out.

I'll be pointing out examples of what's good and what's bad on HN, in the hope
that the feedback will be helpful to the community. When I do that, I hope
everyone understands that it's never personal, only about the content and only
for trying to make HN better.

