Live data from Hacker News

When did people favor composition over inheritance?

sicpers.info

111–120 of 218 posts

Re: When did people favor composition over inheritance?

#111
post #36

Earlier quoted context omitted.

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.

Every language that permits diamond inheritance causes the devs who dare to use this feature at least some nightmare. It's not a C++ issue.

The most evil code I’ve ever written was diamond inheritance where (some) of the base types were template parameters.

I needed it!

For reasons.

Good reasons? No… but I had my justification.

Re: When did people favor composition over inheritance?

#112
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…

> It can easily become a pinball of calls around the hierarchy.

This is why hierarchies should have limited depth. I'd argue some amount of "co-recursion" is to be expected: after all the point of the child class is to reuse logic of the parent but to overwrite some logic.

But if the lineage goes too deep, it becomes hard to follow.

> every time you modify a class, you must review the inner implementation of all other classes in the hierarchy, and call paths to ensure your change is safe.

I'd say this is a fact of life for all pieces of code which are reused more than once. This is another reason why low coupling high cohesion is so important: if the parent method does one thing and does it well, when it needs to be changed, it probably needs to be changed for all child classes. If not, then the question arises why they're all using that same piece of code, and if this refactor shouldn't include breaking that apart into separate methods.

This problem also becomes less pressing if the test pyramid is followed properly, because that parent method should be tested in the integration tests too.

Re: When did people favor composition over inheritance?

#114
When they find inheritance is actually worse at describe the concept though. With composition you no longer need to implement whatever interface and bridge the implementation by proxy or whatever. You are also not limited to what parent class have (while you can still add all components that parent have to children if you need). Interface and proxy is just composition but worse in my opinion

Re: When did people favor composition over inheritance?

#116

It takes about 2-3 years of experience in current enterprise scale to deeply realize that inheritance fundamentally doesn't work.

It depends though. Learning what things don't actually work like the textbooks says is the key to level from junior to senior. Some people never get it, some got it quickly.

Re: When did people favor composition over inheritance?

#117
post #45
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…

This is only the case when the language does not distinguish between methods that can be overridden versus those that cannot. C++ gives you the keyword "virtual" to put in front of each member function that you want to opt into this behavior, and in my experience people tend to give it some thought on which should be virtual. So I rarely have this issue in C++. But in languages like Python where everything is overrid…

While in Python everything is overridable, does this show up in practice outside of (testing) frameworks? I feel like this is way more common in Java. My experience in Python is limited to small micro service like backends and data science apps.

Re: When did people favor composition over inheritance?

#119

Earlier quoted context omitted.

Diamond inheritance is its own special kind of hell, but “protected virtual” members of java and c# are the “evil at scale” that’s still with us today. An easy pattern that leads to combinatorial explosion beyond the atoms in the universe. Trivially. People need to look at a playing deck. 52 cards, and you get 8×10^67 possible orders of the deck. Don’t replicate this in code.

Why do protected virtual methods lead to an explosion?

Protected = subclasses can call them.

Virtual = subclasses can override them.

So basically, any subclass can call the method, and that method may be overridden in any other subclass.

Re: When did people favor composition over inheritance?

#120

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…

Exactly. Inheritance and composition are two different axises of the implementation reuse problem, just as object-oriented programming and functional programming are two different axises of the expression problem. In both cases, you should want to have both axises available, because they do different things. I think saying "prefer composition over inheritance" makes about as much sense as "prefer the Y-axis over the X-axis in a graphics system": it's a statement that doesn't make sense on its own; it only makes sense in specific scenarios, like "... when making a document scroll".

I think the biggest "mistake" in object-oriented programming is the explanations by analogy that many people advocate. A lot of times, people attempt to use a taxonomic metaphor like the tree of life—"a dog is-a mammal" sort of stuff. As a model it falls apart because even the real world tree of life is a flawed model that doesn't fully capture the complexity of life in the way most lay people assume it does. Try getting a random person to justify a platypus being a mammal. Without specific training in biology they stumble. And most computer scientists attempting to employ this analogy are definitely broadly ignorant about biology. You can see it because a lot of the examples people employ aren't even consistent with the metaphor. You're just as likely to see people say things like, "A dog is-a four-legged animal". I think it's an extremely harmful didactic path down which to start.

A lot of these problems happen because most people don't have a good handle on graph theory. They don't understand when they are trying to force a graph with cycles into a tree. Trees are easy for people to understand and handle, graphs with their pesky cycles are much harder, so I get the appeal. But what people come to call "tech debt" or "degenerate edge cases" are really evidence that an inappropriate model was employed early in development.

In real world examples, you'll see object oriented programming and functional programming as well as inheritance and composition used extensively and successfully. I think GUI libraries are a good example here. Buttons and text boxes both inherit from a control base class. This pattern is pervasive and long standing. But you naturally shouldn't usually[1] try to make a form inherit from control as they are much more appropriately compositions of controls.

[1] I say "shouldn't usually" here because some common subforms can be useful encapsulated as a control for composition into other forms, e.g. an address entry form embedded into a user profile form.

Post reply on HN