6 ms·
> Human = torso, legs, arms. Three distinct objects combine into one thing. A human by definition is the union of these things. It's fundamental. It's just your
by josephg 1y ago
> Human = torso, legs, arms. Three distinct objects combine into one thing. A human by definition is the union of these things. It's fundamental. It's just your bias is trying to see it as something else.
No, it’s not. If I put a torso, legs and arms (and perhaps a head) on a table, I don’t get a human being. I’d say a human composes all of those things (and more!). But a human doesn’t inherit from them. For example, each leg can kick(). But you can’t inherit from two legs! And if you did, which leg is the one that kicks when you call the function? Much better to have human class which contains two legs. Then human can have a kick(RIGHT_LEG) function which delegates its behaviour to right_leg.kick().
There’s lots more ways composition helps us model this. Let’s say we want to model blood temperature, which is tracked in every limb separately. Composition makes it more straightforward to have different behaviour (& state) in the Body class for blood temperature than in any of the limbs. (Eg maybe the blood temperature is the average of all limbs temperature. That is more straightforward to implement with composition.)
> inheritance minimizes code copying. It reuses code in the most efficient way possible.
On the surface, I agree with this claim. But it’s funny - programs which make heavy use of OO always seem unnecessary verbose. I wonder why that is? I’m thinking of Java where it’s common to see utility classes that just have 1 or 2 fields take up 100+ lines of code, due to class boilerplate, custom hashCode, toString and isEqual methods and all the rest.
But in any case, as you say, we aren’t just trying to optimise for the fewest lines of code. We’re trying to optimize for how easy something is to read, write and maintain. Adding code to increase simplicity is often worth it. Inheritance increases complexity because it adds a layer of hidden control flow. When I’m reading a program, I need to do a lot of work to figure out if foo.bar() is calling a function in one of the base classes or in the derived class. As you say, humans don’t deal with that kind of complexity well. In general, explicit is better than implicit - this.leg.kick() is more explicit than this.kick() when kick() exists somewhere in one (or more?) of the base classes.
Also let’s say I have 3 classes A, B extends A and C extends A. If there’s a bug in B that involves something in the base class A, fixing that bug may break implicit invariants in C. This kind of “spooky action at a distance” is horrible. Ironically, it violates the principle of encapsulation that OO claims as one of its core principles. I find this kind of problem is rarer in compositional systems. And when it happens, it’s usually much more straightforward to debug and fix. The reason is because classes are all self contained. You don’t have partially-specified base classes that only kinda sorta maintain their invariants. And derived classes don’t implicitly include their base class’s behaviour. As a result, there’s less implicit entanglement. B and C can much more easily change how they wrap A’s behaviour.
At the end of the day, I think we more or less agree that inheritance makes code harder to reason about. I don’t write code to express a pure conceptual representation of the world. (And neither should you!). I write code to get stuff done. If inheritance makes it harder for humans to be productive with our software, then that’s reason enough to abandon it.
- ninetyninenine 1y agoI’m not gonna read the full thing. I’m just responding to the first part. Composes or inheritance is just vocabulary for two things that are the same with slight differences. Composes duplicates identical properties via nesting. Inheritance overrides properties that are identical. That’s it. I’m done.
- josephg 1y agoMaybe that's our ultimate disagreement. I think the difference between composition and inheritance matters a lot. It changes how we break our software into modules. It sounds like you think of inheritance as being fundamentally the same as composition "with slight differences". Although even you admit that "Our brains cannot handle the complexity [of inheritance]". If that's true (and I think it is), the difference is surely more than skin deep. I agree about nesting. But nesting matters, because it forces us to design components which make sense in isolation. As a result, composition encourages - and in many ways requires - better modularity in code. Inheritance does not. Base classes are often poorly conceived, poorly specified grab-bags of state and functions. They lead to hard to understand, hard to follow code. Earlier in this thread you insulted my intelligence. You said this: > Don't ever tell me that programming for 30 years is a reason for being correct. It's not. In fact you can be doing it for 30 years and be completely and utterly wrong. Then the 30 years of experience is more of a marker of your intelligence. I'm curious if you'll still back the argument you've made here after you've been programming for 30 years too. You're clearly already suspicious of how and why inheritance makes code harder to understand. I suspect in a few years, you'll come around to my point of view on this. But I'd love to know if I'm wrong.
- ninetyninenine 1y ago>I'm curious if you'll still back the argument you've made here after you've been programming for 30 years too. You're clearly already suspicious of how and why inheritance makes code harder to understand. I suspect in a few years, you'll come around to my point of view on this. But I'd love to know if I'm wrong. I did insult your intelligence. Because when you said you have 30 years of experience I hear total arrogance. It's like "I'm right and you're wrong because I have 30 years of experience" When I hear that I just want the other person to shut the hell up. >I agree about nesting. But nesting matters, because it forces us to design components which make sense in isolation. As a result, composition encourages - and in many ways requires - better modularity in code. Inheritance does not. Base classes are often poorly conceived, poorly specified grab-bags of state and functions. They lead to hard to understand, hard to follow code. Again, you resuse the same code if you don't use inheritance. A cat walks, so does an animal, do does a dog. You have to write walk() twice if you don't use inheritance. There's a trade off here. The difference is skin deep. It's the emergent complexity that is NOT skin deep. combining objects via "object composition" or "inheritance" is different. One way is not more right then the other. It's simply that you can't handle the hierarchical relationships. But think about it. If you have a deeply nested Object where you don't use inheritance. Then all the objects have multitudes of redundant properties, doesn't that result in complex code as well? And how does nesting objects make it less complex then inheriting objects? It's more of code navigation problem in the sense that when you use inheritance and you look at a child derived from generations of inheritance it's just hard to read and figure out what the final object is. With object composition the view is the same. You have an object that holds generations of nested objects. The difference is you can control click and follow the definition of the nested object so it's more visible. Thus it seems to me the issue with the complexity is that inheritance simply does not give you a widget you can control click into easily to follow the definition. This whole problem could be characterized by a user interface issue because it's not evident to me how an object with nested objects 1000000 layers deep is more complex then the same object derived from 100000 ancestors.