3 ms·
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
by ChrisMarshallNY 1y ago
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 about "The Only Correct Way" can be quite destructive.
- CharlieDigital 1y agoSame way I see it. Objects and inheritance are good when you need big contracts. Functions are good when you want small contracts. Sometimes you want big contracts. Sometimes you want small contracts. Sometimes the right answer is to mix and match.
- strix_varius 1y agoSure, but "favor x over y" or, put another way, "use y only if x is unsuitable" is compatible with this. Nothing in "prefer composition over inheritance" says that composition is the only correct way.
- ChrisMarshallNY 1y agoNo, but I often see folks use it as a bludgeon for dogma.
- bccdee 1y agoI dunno. It's easy to say, "there are trade-offs, it depends" any time two things are compared, and it's never entirely untrue. However, sometimes one option is just generally worse than the other. I'm not saying it's malpractice to use inheritance or anything, but it's a tool I definitely hesitate to reach for. Go and Rust removed inheritance entirely, and I'd say those languages are better-off without it.
- ChrisMarshallNY 1y agoThere’s definitely stuff that it enables. I’ve been writing software since before it was a thing, and it was almost magic, when I first learned about it. I also saw why it fell from grace, but I already knew, by then, that it was no panacea. I learned, on my own, that composition was often a better pattern, and I learned that, back in the 1980s. Not worth arguing about, but I do find absolutism to be almost offensive, and there’s a damn lot of that, in software development.
- deleted 1y ago[deleted]
- morshu9001 1y agoYeah, it's like how OS war truces get proposed. "Depends entitely on the use case." Most of the time, the two arguing over Mac vs Windows or Linux distro A vs B have almost identical use cases. I haven't intentionslly used inheritance in forever, only in cases where some lib forces you to use it that way. Not cause of some trend, but it's just not something you naturally need.
- jeroenhd 1y agoRust removed inheritance only for the Rust ecosystem to generate some kind of half-inheritance system by sticking macros on everything. For every `extends Serializable`, Rust has a `#[derive(Serializable)]`. Superclasses are replaced by gluing the same combinations of traits together in what would otherwise be subclasses, with generic type guards. The problems with bad design don't go away, they're just hidden out of plain view by taking away the keywords. Rust's solution is more powerful, but also leads to more unreadably dense code. One clear example of this is UI libraries. Most UI libraries have some kind of inheritance structure in OO languages, but Rust doesn't have that capability. The end result is that you often end up with library-specific macros, a billion `derive`s, or some other form of code generation to copy/paste common implementations. Alternatively, Rust code just reuses C(++) code that needs some horrific unsafe{} pointer glue to force an inheritance shaped block down a trait shaped hole.
- hgomersall 1y ago
- moron4hire 1y agoExactly. 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.