2 ms·
I agree with this sentiment but I also must say that the time's I've needed inheritance have been few and far between. I have seen really good examples where i
by cogman10 3y ago
I agree with this sentiment but I also must say that the time's I've needed inheritance have been few and far between. I have seen really good examples where it works really well (UX is pretty common, but I've also seen really clean cases like data structures with complex interfaces and a simple abstract class).
Where I think inheritance works best is when the state in base classes is limited and the interface is quiet clear. Ideally where you are meant to override is also well defined.
Where it's the worst is when someone tries to use inheritance because they notice coincidentally duplicate code. The worst hell for this is in application configuration. I've seen 5 layer deep inheritance trees to handle really basic things like "what port should this app bind to". It saved no code and introduced a bunch of complication around the transitive dependency baggage it brought on board.
IMO, configuration should always be done via composition. It's more than fine to have a bunch of smaller composable config pieces just so long as you can easily jettison the broken parts.
- quaunaut 3y ago> Where I think inheritance works best is when the state in base classes is limited and the interface is quiet clear. Ideally where you are meant to override is also well defined. What benefit is inheritance providing here? What you described sounds mostly like a struct, at which point the only value the interface provides is possibly some computed fields.
- cogman10 3y agoWhen you scratch deep enough at programming, everything is structs and interfaces defining how you interact with them and how they interact with the world. The best example of this (IMO) is how `AbstractMap` works in Java. [1] In order to make a new object which implements the `Map` interface, you just have to inherit from the `AbstractMap` base class and implement 1 method, `entrySet`. This allows for you to have a fully compliant `Map` Object with very little involved work which can be progressively enhanced to provide the capabilities you want from implementing said map. This comes in handy with stuff we've done when you can take advantage of the structure of an object to get a more optimal map. For example, a `Map<LocalDate, T>` can be represented in a 3 node structure, with the first node representing the year, the second the month, and the final the day. That can give you a particularly compact and fairly fast representation. The value add here is you can start by implementing almost nothing and work your way up. [1] https://docs.oracle.com/javase/8/docs/api/java/util/AbstractMap.html https://docs.oracle.com/javase/8/docs/api/java/util/Abstract...
- quaunaut 3y agoI understand, and what you shared is a perfect example of what I said- but I fundamentally disagree with the notion that it's the same between the two. I think that in effect, as you associate more behavior with a particular struct(as opposed to what you're attempting to do with said struct), the greater expectation it presents that the struct is what you code around. More and more gets added to state over time, and more expectations about behavior get added that don't need to exist. Sure, you could say "Well, then just be strict about what behavior is expected in the interface"- but that effort wouldn't be necessary if we didn't make the struct the center of the behavior in the first place.
- steveklabnik 3y agoThis works with Rust's traits as well, for example, Iterator. Or Ruby's mixins (which are inheritance, I guess, heh). It is super useful, but doesn't actually require inheritance, even if you can use inheritance to do it.
- SJC_Hacker 3y ago>In order to make a new object which implements the `Map` interface, you just have to inherit from the `AbstractMap` base class and implement 1 method, `entrySet` Used to think that way, but I now prefer the alternative - passing function(s)/lambda(s) for the necessary functionality of the dependent class. This way is actually more flexible, as you can change behavior without modifying the override or having a bunch of switches/if-else in your required function. So instead of 'entrySet' being defined inside MyClass, you would define it outside it, or possible as a static method, and pass it to AbstractMap when you create it. So you don't need to have every class implement a bunch of interfaces like or Hashable, Orderable, etc. in order get the desired behavior. Now I guess you would come back about you shouldn't be able to able to do that outside the class, but I also think those are also bad ideas. Python famous gets away with not having private/protected (although there is a way you can kinda of get something similar).
- microflash 3y agoIt is just a matter of how flexibility you want to expose through your API. Sometimes a rigid and stricter API is the right choice where you want the API itself as the guardrail against non-standard patterns.
- dragonwriter 3y ago> I agree with this sentiment but I also must say that the time’s I’ve needed inheritance have been few and far between. You never strictly need inheritance, but strict need isn’t the criteria for whether something is a good solution (otherwise the fact that most things in programming can be done multiple ways would mean that almost nothing is ever a good solution, since there is almost always an alternative route to the same result.)
- bheadmaster 3y agoI assume the person didn't mean "need" as in "can't do without it", but instead as in "I can make clearer code with it than with the alternative approaches".