Live data from Hacker News

When did people favor composition over inheritance?

sicpers.info

191–200 of 218 posts

Re: When did people favor composition over inheritance?

#191
post #69

Earlier quoted context omitted.

> In swift I can have each object to conform to Hashable, Identifiable, Codabele, etc etc... and keep repeating the same stuff over and over, or just create a base DataObject, and have the specific data object inherit it and just . But then if you need a DataObject with an extra field, suddenly you need to re-implement serialization and deserialization. This only saves time across classes with exactly the same fields…

Yes, all behaviors should be implemented like definitions in category theory: X behaves like a Y over the category of Zs, and you have to recursively unpack the definition of Y and Z through about 4-5 more layers before you have a concrete implementation.

I'll be honest here. I don't know if any comment on this thread is a joke.

There are valid reasons to want each one of the things described, and I really need to add type reflexivity to the set here. Looks like horizontal traits are a completely unsolved problem, because every type of program seems to favor a different implementation of it.

Re: When did people favor composition over inheritance?

#192
post #99

Earlier quoted context omitted.

It's also cultural, possibily. Python supports diamond inheritance, and clearly states how it handles it (it ends up virtual in C++ terms). But in like 20 years of working with Python I can't remember encountering diamond inheritance in the wild once.

Django documentation explicitly recommended it for a short while. At a point, the Python community created all kinds of mixins on all kinds of random APIs. Then people noticed it was bad, and stopped.

Mixins are usually explicitly orthogonal and rarely get subclassed, so diamond-shaped inheritance with mixins seems rare.

Re: When did people favor composition over inheritance?

#193
post #178
post #161

My personal preference for composition over inheritance is that it forces callers to call the owned-object’s methods directly rather than automatically through inheritance. There is more typing/boilerplate but when you read the class file you get a full picture of what’s happening rather than some parts happening automatically in a different file. I like to say that code should be written with a reader bias: the sing…

I call it "read-optimized code". Inheritance is biased toward conservative writing. Once your mind becomes so enmeshed with the code base that you can no longer fathom a future where you might fall out of sync with it, inheritance becomes extremely appealing. It's all in your head! You pull the Razzle parent, sprinkle a bit of Dazzle mixin, everything is alchemized into a Fizzle class and abracadabra. Meanwhile newbi…

That’s a great term and I’ll probably use it instead of mine. Thanks!

Re: When did people favor composition over inheritance?

#194
> That points to a deficiency in the “composition over inheritance” aphorism: those aren’t the only two games in town. If you have procedures as first-class types (like blocks in Smalltalk, or lambdas in many languages), then you might prefer those over composition or inheritance.

First-class procedures/functions are a form of composition. Requiring a function type behaves like requiring an interface/class type with only one method. (In languages like F#, `Func` is literally defined internally as an abstract class with one method, `Invoke`, although there are other mechanisms to auto-inline lambdas when enough static information is available to do so.) In either case, you can place it into a field of an object or data structure, or pass it directly as an argument to a function/method.

Re: When did people favor composition over inheritance?

#195
post #163

Earlier quoted context omitted.

> 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 That's the point: You can reuse code without paying that price of inheritance. You DON'T have to expect co-recursion or shared state just for "code-reuse". And, I think, is the key point: Behavior inheritance is NOT a good technique for code-reuse... Type-inheritance, however, IS good fo…

> You can reuse code without paying that price of inheritance. The same pinball of method calls happens at almost exactly the same way with composition. You save some idiosyncrasies around the meaning of the object pointer, and that's all.

How so? Not sure what you mean.

If object A calls a method of object B (composition), then B cannot call back on B, and neither A nor B can override any behavior of the other (And this is the original core tenet of OO: being all about "message-passing").

Of course they can accept and pass other objects/functions are arguments, but that would be explicit and specific, without having to expose the whole state/impl to each other.

Re: When did people favor composition over inheritance?

#196
post #165

Earlier quoted context omitted.

Diamond inheritance is in fact highly pervasive in Python. The reason is that every class is a subclass of object since Python 3 (Python 2 allows classic classes that are different). So every single time you use multiple inheritance you have diamond inheritance. Some of this diamond inheritance is totally innocuous, but mostly not, because a lot of classes override dunder methods on object like __setattr__. It was Gu…

> Diamond inheritance is in fact highly pervasive in Python. I don't think that's true, because... > So every single time you use multiple inheritance you have diamond inheritance. Multiple inheritance is supported but not itself “highly pervasive” in Python > It was Guido van Rossum himself that observed the prevalence of diamond inheritance The essay you link does not support that claim. He doesn’t observe an exist…

The MRO fix was added to Python 2.3. The new style classes that would cause diamond inheritance to be prevalent were already present in Python 2.2. So they weren’t simultaneous.

A better phrasing would be that Guido predicted the prevalence of diamond inheritance in Python and therefore found it necessary to fix the MRO.

Re: When did people favor composition over inheritance?

#197
post #80

When they put away childish things and read about the SOLID principles. Different time for every engineer.

SOLID is a childish thing, imo. Very undergrad. "Single responsibility" isn't an especially useful yardstick. If you actually need to decompose a complex piece of logic into modules, the place to start is by identifying areas of high cohesion and separating them into loosely coupled functions. Ideally you can match those up to a DDD-style ubiquitous language, so your code will make intuitive sense to people familiar…

Thank you for taking the time to reply, instead of just hitting downvote. I feel like if we argued over a beer we’d probably end up agreeing on a lot of things. But let’s start by disagreeing. :-)

> "Does this have one responsibility?" really isn't the right question to ask.

It’s a great question to ask. As a senior engineer, the answer might be “no”, but there’s a vast difference between code where the answer is “no” because someone made a conscious choice, vs code where nobody even asked the question. Here’s the thing: a compiler and linker can join ten classes into a single executable, but even a senior engineer cannot look at a single class with ten responsibilities and figure out what the fuck is going on. There’s a doc at my company that describes the core function of one particular service. The doc describes the simplest of systems and so you would be surprised to learn that 1) it took me two years of working one the product before I could write it and 2) nobody knew. The reason it took two years was because there were 10 different pathways, and every pathway was just a giant implementation, each written differently, and each, ultimately, doing the exact same fucking thing. But you’d never be sure just by looking at the code. In fact it very much looked like each of these things had very specific things that they did differently. Over two years, while also doing my job of keeping this thing running and adding features, I refactored the thing to be SOLID. In doing so, demonstrated that they all do the exact same thing. We haven’t finished refactoring everything, but we do now test all the pathways with a parallel implementation that verifies 80 classes and 500 instances at runtime with one class and ten instances.

I work on software that you and most people on planet earth with at least a mobile phone are using in one way or another. I have made many pieces of this system better by evolving a clusterfuck of cohesion into a system that is easy to reason about, maintain and evolve - by apply SOLID principles.

I’m currently working on a package used by over 1,000 services. The most pain has been caused by previous iterations ignoring the open-closed principle. As you say, “easy to modify and delete”. A stronger rule, which perhaps you’re alluding to, is don’t allow any extension at all, and just expose only interfaces. In that sense I could agree open-closed principal is moot, but it’s moot for taking its argument to the logical conclusion.

I am also a fan of DDD, and for the reasons you allude to: the second half of the book is more about communicating in a large engineering organization.

Re: When did people favor composition over inheritance?

#198
post #44

Earlier quoted context omitted.

I'm always surprised by how arrogant and unaware Python developers are. JavaScript/C++/etc developers are quite honest about the flaws in their language. Python developers will stare a horrible flaw in their language and say "I see nothing... BTW JS sucks so hard.". Let me give you just one example of Python's stupid implementation of inheritance. In Python you can initialize a class with a constructor that's not eve…

> In Python you can initialize a class with a constructor that's not even in the inheritance chain No, you can't. Or, at least, if you can, that’s not what you’ve shown. You’ve shown calling the initializer of an unrelated class as a cross-applied method within the initializer. Initializers and constructors are different things. > Oh, bonus point. I've see people creating a second constructor by calling `object.__new…

> Knowing that there are two constructors that exist for normal, non-native, Python classes, and that the basic constructoe Class.__new__, and that the constructor Class() itself calls Class.__new__() and then, if Class.__new__() returns an instance i of Class, also calls Class.__init__(i) before returning i, is pretty basic Python knowledge.

I disagree that this is basic knowledge. In python a callable is an object whose type has a __call__() method. So when we see Class() its just a syntax proxy for Metaclass.__call__(Class). That's the true (first of three?) constructor, the one then calling instance = Class.__new__(cls), and soon after Class.__init__(instance), to finally return instance.

That's not basic knowledge.

Re: When did people favor composition over inheritance?

#199

Earlier quoted context omitted.

I have been programming professionally in c++ for 20 years. I remember once thinking "cool, I could use virtual inheritance here". I ended up not needing it. MI is not an issue in c++, and if it were the solution would be virtual inheritance.

Exactly. Unlike Java where every object inherits from Ojbect, in C++ multiply inheriting from objects with a common base class is rare. Some older C++ frameworks give all their objects a common base class. If that inheritance isn't virtual, developers may not be able to multiply inherit objects from that framework. That's fine, one can still inherit from classes outside the framework to "mix in" or add capabilities.…

> in C++ multiply inheriting from objects with a common base class is rare.

One example is COM (or COM-like frameworks) where every interface inherits from IUnknown. However, there is no diamond problem because COM interfaces are pure abstract base classes and the pure virtual methods in IUnknown are implemented only once in the actual concrete class.

Re: When did people favor composition over inheritance?

#200
post #163

Earlier quoted context omitted.

> 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 That's the point: You can reuse code without paying that price of inheritance. You DON'T have to expect co-recursion or shared state just for "code-reuse". And, I think, is the key point: Behavior inheritance is NOT a good technique for code-reuse... Type-inheritance, however, IS good fo…

> You can reuse code without paying that price of inheritance. The same pinball of method calls happens at almost exactly the same way with composition. You save some idiosyncrasies around the meaning of the object pointer, and that's all.

That's not true. If Outer has a member Inner, Outer always has to invoke `my_inner.foo()` to use Inner::foo, and `foo()` always refers to Outer::foo (and some languages will force you to write `self.foo()`, which is even better).

If Outer extends Inner, though, you can't tell whether `foo()` refers to Inner::foo or Outer::foo without checking to see whether Outer overrides foo or not. And the number of places you have to check scales linearly with the depth of the inheritance hierarchy.

Post reply on HN