Live data from Hacker News

When did people favor composition over inheritance?

sicpers.info

121–130 of 218 posts

Re: When did people favor composition over inheritance?

#121
I have been using inheritence for 15 years, and have sometimes regretted it and sometimes loved it.

It does have actual benefits if you can limit its usage, and don't use the full insanity that languages like C++.

I generally dismis people that tell you to always use composition over inheritance without first understanding the problem space, and how it could be modeled.

Re: When did people favor composition over inheritance?

#122
One of the lesser known features in Kotlin is interface delegation. This lets you get away with doing multi class inheritance via composition of a class with a delegate. This kind of blurs the boundaries between inheritance and composition in a useful way.

  class Foo(internal val _list: MutableList=mutableListOf()): MutableList by _list { ... }
Here Foo has a _list property that it delegates the implementation of list operations to. You can even do function overrides in the class and interact with the delegate via the _list property. However, messing with internal list state is off limits (a problem with inheritance).

  val foo = Foo()
  foo.add(1)
Like Java, Kotlin supports single class inheritance. But this provides a way out.

When I was researching this stuff in the nineties, I came across some papers about role based programming by a Norwegian called Trygve Reenskaug. That formed a lot of my thinking on this topic a bit.

Modern Kotlin and Java look a lot like what he proposed: small interfaces (roles) and classes that implement multiple of these things whose objects can play those roles in different contexts. Go's duck typing (having the operations means it implements the interface) is also cool for this. Traits, mixins, etc. are all variations on this topic that you can find in other languages. Javascript is actually a really interesting languages since it is a prototype based language (inspired by a long forgotten language called Self). It did not have classes for a long time (that's a recent syntactic addition) and you create new objects by copying old ones. And since it is dynamically typed, it has no need for interfaces either.

Re: When did people favor composition over inheritance?

#123
post #37
post #30

"Composition" is a word that can mean several things, and without having read the original source I never really understood which version they mean. As a rule, I've always viewed "composition" as "gluing together things that don't know necessarily know about each other", and that definition works well enough, but that doesn't necessarily eliminate inheritance. So then I start thinking in less-useful, more abstract de…

"Gluing together in a way that's not inheritance" is useful enough by itself. Most class hierarchies are wrong, and even when they're right people tend to implement th latest and greatest feature by mucking with the hierarchy in a way which generates wrongness, mostly because it's substantially easier, given a hierarchy, to implement the feature that way. Inheritance as a way of sharing code is dangerous. The thing c…

> Most class hierarchies are wrong

One of the most damaging things is when they teach inheritance like "a Circle is a Shape, a Rectangle is a Shape, a Square is a Rectangle" kind of thing. The problem is the real world is exceedingly rarely truly hierarchical. Too many people see inheritance as a way to model their domain, and this is doomed to failure.

Where it works is when you invent the hierarchy. Like a GUI toolkit or games. It's hierarchical because you made it hierarchical. In my experience the applications where it really works you can count on one hand, whereas the vast majority of code written is business software for which it doesn't really.

Re: When did people favor composition over inheritance?

#124
I've been building gui applications for the past 20 years and I couldn't imagine doing it without an inheritance model. There's so much scaffolding needed to build components and combine them into a working view. Sure inheritance can be bad in the data layer because you don't want to handcuff yourself to bad data expectations. But building out views and view controllers, there's a lot of logic you don't want to keep duplicating every time.

Re: When did people favor composition over inheritance?

#125
post #43

An important point not mentioned by the article is that of "co-recursion" with inheritance (of implementation). That is: an instance of a subclass calls a method defined on a parent class, which in turn may call a method that's been overridden by the subclass (or even another sub-subclass in the hierarchy) and that one in turn may call another parent method, and so on. It can easily become a pinball of calls around t…

I tried to contribute a bug fix to a Common Lisp project and found this exact issue. In CL you can trace methods but if the call hierarchy is several dozen levels deep with multiple type overrides and several :around, :before and :after combinations, it’s just impossible to keep track of what does what. This is not a language issue though, CLOS is really powerful and can be a life saver in good hands, but when people use it just to try the feature it creates monstrosities.

Re: When did people favor composition over inheritance?

#126
post #6

Arguably the answer is “When Barbara Liskov invented CLU”. It literally didn’t support inheritance, just implementation of interface and here we have her explaining 15 odd years later why she was right the first time. I used to do a talk about Liskov that included the joke “CLU didn’t support object inheritance. The reason for this is that Barbara Liskov was smarter than Bjarne Stroustrup.”

There is a reason C++ devs and only C++ devs have nightmares of diamond inheritance. Oh the damage that language has done to a generation, but at least it is largely passed us now.

Curious quirk of history that C++ peaked when Gen X was comiyof age, who were disproportionately affected by lead poisoning.

Re: When did people favor composition over inheritance?

#127
When we realized object models were an anti-pattern. Abstract base classes or just regular class hierarchies inherently create tightly-coupled structures. An eventual maintenance nightmare.

Modularization was the core principle of DDD and it still holds up 20 years later.

Re: When did people favor composition over inheritance?

#128
post #70

Each has its place. There's some things that inheritance makes possible, and some things that are best handled by composition. I use both, quite frequently. It Depends™. Composition can add a lot of complexity to a design, and give bugs a lot more corners to hide in, but inheritance can be such a clumsy tool, that it just shouldn't be used for some tasks. That goes for almost everything in software. Becoming zealous…

I dunno. It's easy to say, "there are trade-offs, it depends" any time two things are compared, and it's never entirely untrue. However, sometimes one option is just generally worse than the other. I'm not saying it's malpractice to use inheritance or anything, but it's a tool I definitely hesitate to reach for. Go and Rust removed inheritance entirely, and I'd say those languages are better-off without it.

Rust removed inheritance only for the Rust ecosystem to generate some kind of half-inheritance system by sticking macros on everything. For every `extends Serializable`, Rust has a `#[derive(Serializable)]`. Superclasses are replaced by gluing the same combinations of traits together in what would otherwise be subclasses, with generic type guards.

The problems with bad design don't go away, they're just hidden out of plain view by taking away the keywords. Rust's solution is more powerful, but also leads to more unreadably dense code.

One clear example of this is UI libraries. Most UI libraries have some kind of inheritance structure in OO languages, but Rust doesn't have that capability. The end result is that you often end up with library-specific macros, a billion `derive`s, or some other form of code generation to copy/paste common implementations. Alternatively, Rust code just reuses C(++) code that needs some horrific unsafe{} pointer glue to force an inheritance shaped block down a trait shaped hole.

Re: When did people favor composition over inheritance?

#129
post #6

Arguably the answer is “When Barbara Liskov invented CLU”. It literally didn’t support inheritance, just implementation of interface and here we have her explaining 15 odd years later why she was right the first time. I used to do a talk about Liskov that included the joke “CLU didn’t support object inheritance. The reason for this is that Barbara Liskov was smarter than Bjarne Stroustrup.”

There is a reason C++ devs and only C++ devs have nightmares of diamond inheritance. Oh the damage that language has done to a generation, but at least it is largely passed us now.

I haven't encountered diamond inheritance a single time in 10 years of writing/reading C++, so I definitely don't have nightmares about it. Maybe that was really a thing in the 90s or 2000s?

Re: When did people favor composition over inheritance?

#130

One of the lesser known features in Kotlin is interface delegation. This lets you get away with doing multi class inheritance via composition of a class with a delegate. This kind of blurs the boundaries between inheritance and composition in a useful way. class Foo(internal val _list: MutableList =mutableListOf()): MutableList by _list { ... } Here Foo has a _list property that it delegates the implementation of lis…

Though it's important to add that composition is not a complete replacement for inheritance, see the Self problem. (The Manifold project has a good description on it).
Post reply on HN