Hacker News new | ask | show | jobs
by charlysisto 3620 days ago
It's funny to see in the life cycle of every new language/framework in the block the rediscovery of good architectural patterns that can be summed up in one sentence: - inheritance & mixin bad / composition good.

Good article nevertheless.

2 comments

> inheritance & mixin bad / composition good

This wisdom is at least as old as the classic GoF book [1] which was published in 1994. Many of their patterns can be summarized as: "Favor 'object composition' over 'class inheritance'."

They also give a pretty simple, compelling reason for that: It is just a direct consequence of the even more fundamental principle:

"Program to an interface, not an implementation."

[1] Gang of Four (Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides): "Design Patterns: Elements of Reusable Object-Oriented Software" https://en.wikipedia.org/wiki/Design_Patterns

I would argue that the more intuitive principle behind composition is the "Single Responsibility" one. Although it is correlative to the idea of 'program to an interface' (aka don't mingle with the state from the inside of an object that is not for you to do) I find it easier to grasp.

While i'm at it I'll add that the main reason SRP is so important is because it limits combinatorial explosions of state, so you can keep control, test and reason about it - incidentally the main idea behind the success of React.js

I find that inheritence allows me to save a ton of code, but using it widely does mean you need the ability to refactor, which isn't hard in Java or C#, but can be more of a pain in languages like Ruby.
inheritance is very hard to comprehend at a later date, traversing though sub/super class hierarchy, overrides and super calls is incredibly painful.

You often end up with a subclass that may be thousands of lines in length if you consider it's flattened definition.

This hasn't been my experience at all. Most class hierarchies I've seen don't got much further than 2-3 levels deep, and each level deals with a specific level of functionality. In most cases, the last descendant class contains the functionality that the end class "user" is most likely to be interested in, while the ancestor classes are most likely to contain foundation code/data that is primarily used by the descendant classes internally (but not always, of course).

Also, most modern IDE's have a "Find Declaration" or something similar that allows very simple traversal of class hierarchies. And the help systems are built to allow for easy navigation of the hierarchy so that you can visualize the inheritance paths.