5 ms·
>In all my experience with OOP, it's always been inheritance that is the root of all evil. It's not, though, and the fact that people keep repeating this meme
by colllectorof 5y ago
>In all my experience with OOP, it's always been inheritance that is the root of all evil.
It's not, though, and the fact that people keep repeating this meme shows that most developers don't even bother thinking about issues they face beyond superficial blamesplaining.
The reason inheritance causes so many issues in languages like Java is because they are statically typed and also use classes as types[1]. Classes must be somewhere in the inheritance tree, hence you are forced into some place of that tree. To make things worse, Java has many keywords that restrict what inheritor of a class can do (private, final, etc).
Inheritance is much less troublesome in, say, Smalltalk, since the language is dynamically typed. If someone expects you to implement Foo, you can (almost always) just implement its relevant methods without explicitly extending the class. Thus, a whole host of annoying scenarios simply does not occur.
--
[1] BTW, this breaks one of the fundamental commandments of classic OOP: you should not depend on implementation details of an object, only on its message protocol. Obviously, it's impossible to be independent of implementation details if some library forces you to use a particular class.
- bccdee 5y agoYou're describing interface-based polymorphism, which is what go and rust use. In go, I can have a struct with methods that implements a particular interface by implementing all the methods described in that interface, but I can't inhereit from another struct. The person you're replying to called this out as a better system too.
- SPBS 5y ago> Inheritance is much less troublesome in, say, Smalltalk, since the language is dynamically typed. If someone expects you to implement Foo, you can (almost always) just implement its relevant methods without explicitly extending the class. Sorry, I don't understand this sentence. Isn't inheritance simply a way to avoid writing duplicate code? If you write the code to implement methods, isn't that not inheritance anymore?
- throwaway894345 5y agoInheritance conflates code reuse ("avoid writing duplicate code") with polymorphism (allowing for multiple different instances to implement the same interface). It also allows for trampolining method calls up and down a hierarchy (a method in a base class might call another method which might be overridden by another class in the hierarchy). Outside of OOP, we use composition for reuse and interfaces for polymorphism, and we don't trampoline method calls up and down a hierarchy because it's (probably?) always a bad idea. When we really need reuse and polymorphism, we can use both composition and interfaces, since the two are correctly orthogonal.
- deleted 5y ago[deleted]
- spacechild1 5y ago> Inheritance conflates code reuse ("avoid writing duplicate code") with polymorphism (allowing for multiple different instances to implement the same interface). Note that languages like C++ allow for inheritance without polymorphism, i.e. pure implementation inheritance. However, I also think that composition should be preferred whenever possible.
- ratww 5y agoWhat the grandparent post means is that in dynamic languages you can just implement one of the "base" methods yourself instead of inheriting from a class that's bigger than you need, in order to avoid problems. I personally don't have an opinion on that, but it's not something I'd do myself. Also, like the sibling said, inheritance is a tool that does multiple things: code reuse, which we call implementation inheritance, being the one everyone hates (the age-old advice is to use composition for code reuse instead), and interface inheritance being the one everyone loves.
- throwaway894345 5y ago> It's not, though, and the fact that people keep repeating this meme shows that most developers don't even bother thinking about issues they face beyond superficial blamesplaining. I don't know that I'd say "inheritance is the root of all evil" (there are lots of antipatterns in OOP that are unrelated to inheritance, like Joe Armstrong's banana-gorilla-jungle observation) but I will say that inheritance is pretty close to useless in the best case and harmful in most cases. And I say this as someone who learned to program and then became a professional programmer when OOP was all the rage. I was taught OOP without the previous bias of other paradigms; it was only after learning other paradigms that I was able to articulate frustrations I was having with OOP. The implication that people who criticize inheritance in this way "haven't bothered to think" is patently false in the best case, and laughably arrogant in the worst case. > The reason inheritance causes so many issues in languages like Java is because they are statically typed and also use classes as types[1]. Classes must be somewhere in the inheritance tree, hence you are forced into some place of that tree. To make things worse, Java has many keywords that restrict what inheritor of a class can do (private, final, etc). Fear not, Python is dynamically typed and inheritance is a mess there as well. > If someone expects you to implement Foo, you can (almost always) just implement its relevant methods without explicitly extending the class. This is just structural subtyping (see Go's interfaces for a statically typed example of structural subtyping) also known as "duck typing". It seems like you're positing that the problems with inheritance derive from nominal subtyping (e.g., Java's `implements` keyword), but these things are orthogonal. Python has duck typing ("structural subtyping") and its inheritance is no less painful than Java's. Similarly, Rust has nominal subtyping (a type must explicitly implement a trait) and it has none of the inheritance-related problems that Python and Java have.
- foobarian 5y agoI feel like OOP always had the nerd catnip problem. Since the very beginning the various programming tutorials would have the contrived examples of animals and canines and dogs, or geometric shapes and triangles etc. which just managed to ring a particular very satisfying bell in people's heads. It was just such a neat concept with those examples that just made sense. How it turned out in practice is a different story but I feel this had a lot to do with the enthusiastic uptake.
- philwelch 5y agoTo me there are about three tiers of this basic insight: 1. Inheritance causes all kinds of issues so you shouldn’t use it. 2. Actually, inheritance is fine as long as you do it right (e.g. Liskov) 3. Actually, getting part 2 right is difficult, and the heavy risks of getting it wrong aren’t worth the minor benefits of inheritance.
- nine_k 5y agoPolymorphism is good. You describe polymorphism. Inheritance is bad. Inheritance is patching a class and overriding some of its methods, while leaving others intact. This brings all kinds of unexpected interplay between methods of different levels of overriding. A typical example is http://www.cse.psu.edu/~deh25/cmpsc473/jokes00/joke01.html http://www.cse.psu.edu/~deh25/cmpsc473/jokes00/joke01.html Ideally all "concrete classes" with method implementations should be final, and the polymorphism should be achieved via interfaces / typeclasses / traits, or purely abstract classes where these are not available. Reuse of implementation should be achieved via composition; there are several ergonomic ways to express it.
- piotrkaminski 5y ago> Simulator supervisors report that pilots from that point onward have strictly avoided kangaroos, just as they were meant to. Won't fix; working as intended.
- heavyset_go 5y ago> Polymorphism is good. You describe polymorphism. It also seems like they're describing the nominal typing of Java versus a structural approach.
- nine_k 5y agoHaskell and, IIRC, Rust allow you to declare that a certain data type conforms to some interface, and describe how, by listing / adding the functions with necessary signatures. This allows to have the upsides of structural polymorphism without losing static checks. Go, OTOH, goes all the way structural.
- AdieuToLogic 5y ago> Haskell and, IIRC, Rust allow you to declare that a certain data type conforms to some interface ... I believe at least in the case of Haskell, you are referring to type classes[0]. 0 - https://wiki.haskell.org/OOP_vs_type_classes https://wiki.haskell.org/OOP_vs_type_classes