7 ms·
> But assume there are 3 objects with distinct properties... Why would we assume that? If the objects are entirely distinct, why are you combining them togethe
by josephg 1y ago
> But assume there are 3 objects with distinct properties...
Why would we assume that? If the objects are entirely distinct, why are you combining them together into one class at all? That doesn't make any sense. Let distinct types be distinct. Let consumers of those types combine them however they like.
> But fundamentally there's no OTHER SIMPLER way to merge two objects without resorting to complex nesting. [...] You have two classes A and B and both classes have 90% of their properties shared. What is the most fundamental way of minimizing code reuse? Inheritance. That's it.
So now the objects have 90% of their properties shared. That's different from what you were saying earlier. But moving on...
The composition answer is similar to the inheritance answer. Take the common parts and extract them out into a self contained type. Use that type everywhere you need it - eg by including it in both A and B.
> given this: A = {a, b, c, d} I want to create this: B = {a, b, c, d, e}. [...] What's the best way to define B while reusing code?
Via composition, you do it like this:
B = { a: A, e }
What could be simpler than that?
You keep claiming that inheritance is simpler. But then you go on to say this:
> The problem with inheritance has to do with human capability. We can't handle the complexity that arises from using it extensively. [..] It is human nature or our incapability of DEALING with the complexity that arises from it. The issue is the coupling is two tight so you make changes in one place it creates an unexpected change in another place. Our brains cannot handle the complexity.
In other words, using inheritance increases the complexity of the resulting code for the human brain. It makes our code harder to read & understand. I absolutely agree with this criticism you're making.
And that's an incredibly damning criticism, because complexity is an absolute killer. Your capacity to wrangle the complexity of a given piece of code is the single greatest limitation of any software developer, no matter how skilled. Any change that makes your code more complex and harder to understand must be worth it in some way. It must pay dividends. Using inheritance brings no benefit in most cases over the equivalent compositional code. The only thing that happens is that - as you say - it makes the software harder for our brains to handle.
If you ask me, that's a terrible decision to make.
- ninetyninenine 1y ago>Why would we assume that? If the objects are entirely distinct, why are you combining them together into one class at all? That doesn't make any sense. Let distinct types be distinct. Let consumers of those types combine them however they like. 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. >So now the objects have 90% of their properties shared. That's different from what you were saying earlier. But moving on... So? I can talk about multiple things right? This is allowed in life right? Did i break the law here? >The composition answer is similar to the inheritance answer. Take the common parts and extract them out into a self contained type. Use that type everywhere you need it - eg by including it in both A and B. Composition is the same thing. But it's saying instead of overriding duplicate properties, Clone the duplicate property. That's it. And it uses nesting to achieve this. This arbitrary rule isn't more fundamental then overriding the duplicate property. >Via composition, you do it like this: Why don't you take a look at my examples again. You are either not able to comprehend or you didn't read it. I literally said the same thing: B = {a: A, e} The above a complete dupe of what I wrote. B = {a, b, c , d, e} Just nested. Which I brought up: B = {{a, b, c, d}, e} Is it not? Please read my response replying with stuff like this. Read it thoroughly. >And that's an incredibly damning criticism, because complexity is an absolute killer. Not exactly it's not that straightforward. Because inheritance minimizes code copying. It reuses code in the most efficient way possible. So actually lines of code and duplicate code actually goes down. So complexity in one area falls and rises in another area. Our brains are biased towards handling complexity of duplicate code better then tightly coupled code.
- 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.