22 ms·
If Inheritance is so bad, why does everyone use it?
- signa11 3y agohuh :o) the wisdom since late 90's has been to avoid it like a plague, at least in c++ circles. for example, see numerous articles in 'guru-of-the-week' by herb-sutter. the same wisdom can be applied elsewhere as well, there is no need to keep discovering the same truths in different contexts again, and again.
- stingraycharles 3y agoYeah in C++ especially nowadays using compile-time traits is much, much more idiomatic.
- constantcrying 3y ago>huh :o) the wisdom since late 90's has been to avoid it like a plague I am sure that hundreds of thousands of students have at some point heard that these hierarchical organization of data is how you should model a system.
- est 3y agoIMHO inheritance works and only works for graphics related programming, e.g. GUI and games. Visual objects can be abstracted in the manner of inheritance.
- mattnewport 3y agoInheritance doesn't work well for games, that's why so many games take a component based approach like Unity, or full blown ECS.
- lewispollard 3y agoWell, it does work well for the engine part of game engines, and graphics related code in particular as the GP says - it's just gameplay code where OO falls a bit flat.
- ipaddr 3y agoUnless it'a role playing game.
- lewispollard 2y agoI don't follow - role playing games are often most suited to ECS, because you want the kind of emergent gameplay that independent gameplay systems acting on component composition gives you - and the kind of flexibility it allows in composing weapons, spells, buffs/debuffs, and so on, vs a strictly hierarchical OOP approach.
- mattnewport 3y agoNot really, the data oriented design movement in games programming that eschews OOP is driven by performance concerns of traditional OOP code on areas like rendering. As a graphics programmer for many years I can also say that OOP is not really a good fit design wise for modern rendering engines, it mostly just gets in the way.
- lewispollard 2y agoI only really know of Bevy that uses ECS for the rendering backend, but iirc even that doesn't really use entities and components, but rather its resource system, such that rendering commands are stored in the ECS "world" but as globally unique resources, rather than entities and components. But, my knowledge doesn't extend to AAA engines, perhaps things are done more extensively there.
- professoretc 3y agoThe implementation of inheritance (more specifically, polymorphism and dynamic dispatch via vtables) is a problem in games, because it adds an extra layer of indirection, and screws with cache locality. But the semantics of inheritance (X ISA Y) still apply. In an ECS, the implementation is different (struct-of-arrays) but you can still think of an entity as "inheriting from" the various components its built from.
- tubthumper8 3y ago> you can still think of an entity as "inheriting from" the various components its built from. If it's based on components, wouldn't that mean you would think of it as being... _composed_? I typically don't hear "it's made of many components" and think of inheritance
- mattnewport 3y agoThat's not really accurate. Component based systems are designed specifically to address situations that traditional inheritance doesn't handle well. Examples are usually something like having a base entity, a player that inherits from entity and an enemy that inherits from entity. Then you have a magic user and a barbarian inherit from enemy but now you also want your player to be able to use magic. Traditional OOP doesn't make it easy to share the implementation. Even with composition and interfaces you still have problems with most traditional OOP languages when you want to do things like change the set of components of an entity dynamically at runtime (player gains or loses the ability to use magic during the game). "Is a" is often not the relationship you want to model. A player and an NPC both have the "has a" relationship to an inventory, not the "is a" for example.
- fxd123 3y agoThat's strange because I was about to comment the exact opposite. ECS is for games, inheritance is for almost everything else
- ipaddr 3y agoFor games having your character as a type of a general class fits well. Having enemies be a type of a class with a base of abilities can fit well too. Weapons, etc all module well. Game play. Scenes.
- davedx 3y agoThis has more or less been my experience too. Things like UI toolkits or game engine primitives are often solved nicely with inheritance. I've been doing a lot with LeafletJS lately, and it has some light inheritance around its drawing primitives (things like boxes, circles and lines you draw on top of maps). It works well. For 3D engines you still need to be quite careful with where you apply it though. It's possible to get into a huge mess if you use it too much.
- flohofwoe 3y agoEspecially in games, inheritance doesn't work all that great and had been replaced by composition via components a long time ago where each component implements one specific aspect or feature of a game object and can be (more or less) freely combined with other components.
- KevinMS 3y agoand it became popular at just about the same time as GUI programming, coincidence?
- cess11 3y agoThe picolisp database convinced me that it also can work for databases, where it's used both for data storage, querying and as a GUI backend.
- doomlaser 3y agoLots of design patterns have their uses, but can suffer when applied too overzealously.
- JauntyHatAngle 3y agoLike almost anything in IT, if you are good at using a hammer, you treat everything like a nail.
- chii 3y agoif you're good at using a hammer, the nail will be hammered in perfectly and without faults. The only problem is if you're bad at using the hammer, and you only know how to use a hammer.
- high_na_euv 3y agoDo they? I rarely see people using it directly and I think this is fine
- angiosperm 3y agoMost of us have no contact with Java. But Java offers little else than inheritance to use to organize a system, so Java coders use it for everything. They are not exactly wrong, except in using Java at all. But sometimes is all that is allowed.
- pgwhalen 3y agoThis is much less true for modern Java than it used to be, with records, pattern matching, sealed classes, etc. The trick though is you actually have to use modern Java, which means you need to both be on the right version of Java, and have developers that understand the value/power of these newer constructs. Which is surprisingly rare for a programmer that self-identifiers as a Java programmer.
- bedobi 3y agojava offers composition, like pretty much all programming languages if you have a person class and you have students and teachers the correct thing to to is NOT to make student and teacher inherit from person it's to make student and teacher have a person attribute + the other attributes that constitute a student and teacher respectively
- kuang_eleven 3y ago"correct" is certainly not the right way to put that. Inheritance and composition here are both fully valid methods for modeling the relationship, and the decision to use either should be dependent on how the models are being used and the expectation for future extension.
- angiosperm 3y agoBy "correct" here you only mean fashionable. Either approach works. Each has its merits and its costs. Any big enough system will have parts that are most sensibly built object-oriented, and other parts that are more reasonably functional, plus anything else you can think of.
- KorematsuFredt 3y agoHere is my take on it. At one point "Object Oriented" became "Blockchain" (or now Gen AI) of those times. You had to be "object oriented" in order to be taken seriously. This applied to everything. Even finished software products were called "built using object oriented". The esoteric concept of inheritance became popular after that. At some point it became so popular that people figured out that it is not really a good thing.
- blowski 3y agoPragmatists used inheritance because it gave a quick benefit now, and the longer-term costs were ignored. Some pragmatists became successful because they moved quickly, so the "Programmers from the Church of Purity" used inheritance because successful companies used inheritance. When inheritance was no longer the quickest way to move quickly, the pragmatists moved on. The Church of Purity now bangs on about Functional Programming in the same way for the same reasons.
- mewpmewp2 3y agoFP rather slows things down though.
- pyrale 3y agoWhat specifically slows it down in your context?
- mewpmewp2 3y agoDoing every simple thing that used to be done in "normal procedural" way in functional paradigm instead when using something like Scala or fp-ts in TypeScript. Causing engineers to completely change their mental model of how the code runs, which I still have intuitive trouble imagining correctly and I see it with other developers as well. A lot of energy goes into trying to understand how to do a simple action. It is much harder to read the code and have a correct mental model as well.
- fardo 3y agoI think there’s a tension between the tinkerer, blogger and hobbyist side of the field, and anonymous 9–to-5 workhorse line of business programming in this discussion. The core reason everyone uses it seems to me to be essentially the same reason the former groups hate it - it’s what everyone else does, it’s an incredibly square and “day-job”-esque approach to one’s tooling and architecture, it often reeks of bureaucracy, compartmentalization, and organizational superstructures often unrelated to the technical work, dictated primarily by managerial fiefdoms and needs to organizationally coordinate, and it’s a well-defined space with extremely predictable solutions and headaches, and therefore has very little excitement or novelty to offer.
- Animats 3y agoMany of the headaches associated with Rust come from an inability to reference from owned to owner. Affine types have much more impact on design than inheritance vs. composition.
- bl4ckm0r3 3y agoOOP is easy to understand and explain and it makes sense initially, plus a lot of people specialize in languages/frameworks that enforce it, so it becomes easy to get trapped in the OOP world. It also usually comes with DDD (which is a solution to a problem you shouldn't have had in the first place) which is a way to limit the damages of OOP into contextual areas. I also think the blanket statement (OOP is bad) does not apply necessarily everywhere. A good mix of composability and a bit of inheritance where it makes sense is the best way imo.
- cess11 3y agoI disagree. Starting with separating class and object have never been helpful when I've tried to teach computer programming, while functions seems to have been more intuitive to those people. After a while you get to closures, which are pretty much objects without classes, and then factory functions that produce closures and there you have something like a class as well. If I were to design a course I'd probably follow this flow to get to 'OOP' in that sense.
- xwolfi 3y agoBut you go from the bottom up here, I don't like to describe it that way. I prefer to say that we naturally classify objects around us using fuzzy boundaries like "a house", "a cat", which don't exactly mean anything: they are templates we use later to actually generate an actual house, or an actual cat (on a piece of paper as a drawing for instance). The world being separated between our internal classes and the interactive instances of them, we can code that way too: we look for what we actually need in a concept, name it and define only what we need, then instantiate several concrete actors interacting together. Each actor is complete, well defined, with contracts for interactions. You can then start building on top of this more complex systems, talking in English and defining rationally the world of your little model. If you start talking about functions, you're basically aliasing a memory address for a bunch of code: you'll goto that function address, with some parameters at some other address, do a bunch of things and put the result in another address for the next function to process. You're building at best a suite of pipelines, which ends up being a little bit too technical and static for my taste. Another way to defend modelling a software with classes and object, in my view, without trying to talk about functions, is the blank page effect: imagine arriving at random at some assignment. You understand vaguely the business problem, now you need to code a solution. You have 6 months, will generate a few millions a year and will require a team of 10 people eventually to test, maintain, operate and debug: you start with defining functions generating functions with callback parameters in Python, or you launch IntelliJ and you do a Java class model ? I'd be terrified modelling complex problems with just functional pipelines, I think it's just way more obvious to talk about objects at all time if you're gonna do something that is not just processing inputs into well defined outputs.
- zelphirkalt 3y agoSome modern programming languages do not even have classes to inherit from. Instead they have concepts like traits and structs, which decouple structure and behavior.
- julienreszka 3y agoNowadays I have rarely seen it if ever
- cess11 3y agoSmalltalk having a more or less revolutionary developer experience was likely quite important. A graphical interface to your system where you can traverse a tree and inspect _everything_, and more importantly, change it. In Smalltalks as I know them, which is sporadically and shallowly, inheritance seems to be used as a factoring tool, when you break down functions into smaller functions there's a structure in which to place them with the implicit incentive to put them as high up as possible. To me it seems to work pretty well, but it's also a rather weird environment where even classes themselves are objects. In Java one can get around inheritance by using injection into an attribute in a wrapper class that extends or changes an object API. This leads to a similar decoupling as more functional composition, but you pay by having more objects swimming around in the VM during execution. Extending on the class level (i.e. in declarations of classes, interfaces, traits and the like) instead of the object level might have effects on performance and resource management. Usually that likely doesn't matter, but it might. In Java-like languages I tend to use inheritance as a quick way to abstract over or extend functionality in a library class, I get the public methods 'for free' in my own class without having to write the wrappers myself. And that's usually where I stop, one level of inheritance, since Java isn't as inspectable and moldable as, say, Pharo, and doesn't give the same runtime ergonomics with large class/object trees.
- moshegramovsky 3y agoI work on a very large codebase. We use inheritance and composition. I totally agree that inheritance should be used carefully. However, there are times when composition requires a lot more code and downstream maintenance. Let's stay I have a base class A. Let's say I have 70 classes that inherit from A. These classes must serialize/deserialize from disk. My choice here is that I can add a new data member to A and then update the read/write method in one place, or I add a new data member to all the classes that are derived from A, meaning that I get to update 70 classes. Seriously? Who would think this is a good idea? You'll have to write 70 times as much code... there will be times when that's definitely a very bad idea. Maybe don't use inheritance. Then give every class its own "Name" data member like std::string c_nName. Every class can have its own function to set the name, or we could use an unencapsulated free function and do things like C. Except then the experts will say things like "well, now you're doing C with classes". Then you get a ticket that the user can enter bad names. You can then figure out some way to validate the names, right? That could be a free function, or maybe write some class called NameValidator. Except now the experts say you're writing C++ like Java. Too many classes, too few classes, too many objects, too few objects, too may free functions, too few free functions. C++ expert of the month may say X, Y, Z, but look at Microsoft's APIs. Inheritance is everywhere. Look at any open source C++ project. Inheritance is everywhere. Does that mean that you should make something like: A --B : public A ----C : public B ------D : public C --------E : public D ----------F : public E Or this: A B \ / C Not unless you have a damn good reason. But the point is that inheritance is a valid tool and it can reduce bugs, code duplication, maintenance. Right tool for the job, yeah? I once read something in the C++ FAQ that said something like "Don't take advice from people who don't understand your business problems."
- drewcoo 3y agoNot even wrong. Inheritance isn't necessarily bad. If anything is common, someone will try to seem like a brilliant iconoclast by writing an essay against it. The particular essay in this case was from a Pythonista (not surprising, given Python's weird love/hate with objects): https://solovyov.net/blog/2020/inheritance/ https://solovyov.net/blog/2020/inheritance/ And not everyone uses inheritance. Maybe you have another question, like "why would someone use inheritance?"
- peter-m80 3y agoSpeak for yourself, I don't use it.
- bigstrat2003 3y agoI don't think inheritance is bad at all. It is very often the easiest way by far to model a problem. Sure, it's not perfect, but I think it is wildly overhated by a vocal minority.
- neonroku 3y agoDepends on how you use it, but in Java I prefer to use interfaces and no subclassing. I find classes with abstract methods hard to reason about, and much prefer to leverage Functions/lambdas where possible.
- arethuza 3y agoHave you ever seen wildly over-architected OO code where everything seems to be an abstract class and it seems impossible to find out where stuff actually happens? Inheritance is like a lot specialised tools - it can be useful in some situations but I think those are rarer than supporters might thing. Completely refusing to use inheritance and always using inheritance both seem like extreme views that should be avoided.
- pyrale 3y agoYou haven't lived life to the fullest until you've had to debug an issue in a 12-layer-of-inheritance class with the original call ping-ponging a couple dozen times across the layers and overrides everywhere. I guess one could say this would be workable with proper tools, but the IDEs just aren't there. Move up a level in inheritance, ctrl-click on a call? It was overriden somewhere in the hierarchy, but the IDE will send you to the parent-class definition regardless.
- touisteur 3y agoI have actually seen one (1) beautiful C++ codebase with very good use of OOP and multiple inheritance, and author (I was his intern) painstakingly taught me about inheritance and its good uses. He used Design and Evolution of C++ (!) as a bludgeon. It was 2006.
- 3y ago
- pvdoom 3y agoIDK, I recently did a big presentation on the topic in my company's Python community and there were tons of people that were genuinely surprised it's not great. And people in my department love throwing random classes that inherit from everything in the universe for no reason at all... for python. Like not even Java or C#. And coming in as the lone new-comer its not going to be easy to convince everyone that has been there for years that the common sense aint great.
- jillesvangurp 3y agoKotlin has a few nice patterns here. It allows inheritance but only if you mark your class as open (it's closed by default). This prevents people inheriting from things that weren't designed from that. It also has extension functions, which allows you to add functions and properties to types without having to inherit from them. This is very nice for fixing things that come with Java libraries to be a bit more Kotlin friendly. Spring offers a lot of Kotlin extension functions for its Java internals out of the box. Another nice pattern is interface delegation where you can implement an interface and delegate to another object that implements that interface: class Foo(private val myMap: Map<String,String>=mapOf()): Map<String,String> by myMap This creates a Foo class that is a Map that delegates to the myMap property under the hood. So you side step a lot of the issues with inheritance; like exposing the internal state of myMap. But you can still override and extend the behavior. Replacing inheritance with delegation is built into the language. The net result of this is that inheritance is rarely needed/used in Kotlin. It's there if you really need it but usually there are better ways. Scala and other languages of course have similar/more powerful constructs. But Kotlin is a nice upgrade over Java's everything is non final by default (or generally defaulting to the wrong thing in almost any situation). Go has a nice pattern based on duck typing where instead of declaring interfaces, you can just treat something that implements all the right functions as if it implements a type. Rust similarly has some nice mechanisms here. All these are post-Java languages that de-emphasize inheritance.
- frizlab 3y agoSame with Swift
- pdpi 3y agoOne of the ways that I think Kotlin and Rust are objectively better than Java and C++ is in that they have saner defaults than their predecessors (like open/final and mut/const). I've lost count of how many talks I've watched by Kate Gregory where she advocates for people tagging everything they can as const in their C++, but asking people to eat their veggies never works.
- jonnycomputer 3y ago
- v1sea 3y agoThe article has a nice history review of inheritance, but it would have been useful for there to be some concrete examples. The worst example of inheritance I've ever found is in java Minecraft's mobs[0]. It is very deep in some places and has numerous examples of sub classes not fully implementing their parent interfaces. Example 1: Donkey[1] Donkey > Chested_Horse > Abstract Horse > Animal > Ageable Mob > Pathfinder Mob > Mob > Living Entity > Entity Example 2: Blaze[2] Blaze has a bit for 'Is on fire', but how is this any different from Mob 'Is aggressive'? Blaze > Monster > Pathfinder Mob > Mob > Living Entity > Entity [0] https://wiki.vg/Entity_metadata#Entity https://wiki.vg/Entity_metadata#Entity [1] https://wiki.vg/Entity_metadata#Donkey https://wiki.vg/Entity_metadata#Donkey [2] https://wiki.vg/Entity_metadata#Blaze https://wiki.vg/Entity_metadata#Blaze
- Sakos 3y agoBoth of those look like perfect cases for composition instead of inheritance.
- coffeebeqn 3y agoAnyone not using components for game objects is insane. How do you add a fire breathing horse without that
- feoren 3y agoYes, now take it further. Anyone not using components for all domain modeling is insane. Why does everyone assume this wonderful practice only applies to games?
- maeil 2y agoAny recommendations for where we can learn about this approach, or recommended search terms? I'm struggling to find anything through Google that isn't about the Unity component system in specific. Is it this? [1] https://en.wikipedia.org/wiki/Entity_component_system https://en.wikipedia.org/wiki/Entity_component_system
- shuvuvt5 3y ago[dead]
- NotGMan 3y agoBlack and white extremist views. Sometimes inheritance is extremely useful. Sometimes it's harmful. Sometimes it's not the best but the most practical nontheless. Language concepts are tools. These people with extreme black and white views live under the delusion that there can be some "Perfect Language which will cause the program to never develove/get technical debt TM". Pure delusion. Real life is an approximation where you do what is best in practice, not in some delusional daydream of perfection which breaks the moment you try and bring it to reality.
- nusl 3y agoI quite like inheritance personally, assuming that it's done reasonably well and isn't a confusing mess.
- captainmuon 3y agoI feel people don't understand what inheritance and (object orientation in general) is useful for, misuse it, and then it gets a bad reputation. It's not about making nice hierarchies of Cars, Fruits, and Ovals. For me the main point is (runtime) polymorphism. E.g. you have a function that takes a general type, and you can pass multiple specific types and it will do the right thing. And if you want to avoid huge if-else statements, you should put the code for the special cases in the classes, not in each function that operates on them. You can get this without implementation inheritance, it is also possible to just have something like interfaces. But I do find it very convenient to put common code in a base class.
- bluetomcat 3y agoPolymorphism is doable in plain old C with lookup tables and function pointers. If that is the only benefit, what is the point of creating a language where everything is an object?
- mrkeen 3y ago> Polymorphism is doable in plain old C with lookup tables and function pointers Not without casting. qsort is still: void qsort_r(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *, void *), void *arg);
- chuckadams 3y agoPresumably one would write an object-oriented version of quicksort to go with their OO C library design and not use the stdlib functions. For example GObject is OOP in C, and I imagine it has collection types with sort methods. Inheritance is what will bite hard when hand-rolling OOP in C. For one, you can forget about the compiler enforcing substitutability and co/contravariance for you.
- vidarh 3y agoBecause syntactic sugar and abstractions matter. You can do everything in assembly too, yet we prefer something higher level.
- datascienced 3y agoInheritance is not so bad for native UI stuff where it makes sense for the class libraries. But in work code I don’t see it much. If it is used it is light weight. Often used incorrectly for splitting code into different files rather than actual inheritance (giveaway is one one derived class!)
- deleted 3y ago[deleted]
- nertirs 3y agoI only tend to see inheritance in engines and libraries, where it makes sense to create more generic, reusable and composable code, since most of the functionality in these is defined by technical people. It makes no sense to use inheritance in the business layer, because a single feature request can make a lot of the carefully crafted abstractions obsolete.
- photonthug 3y agoI've never seen it put quite like this, but it feels right and is refreshingly concrete. Trust the abstractions you can actually design/control for, treat all the other ones as suspect. One still needs the wisdom to tell the difference, but at least focusing on "feature request" focuses the mind. This is at least simple even if it is not "easy". An argument against OOP where you first need to define/explain differences between composition/inheritance, Liskov Sub, compare and contrast with traits, etc is not really that effective when trying to mentor junior folks. If they understood or were interested in such things then they probably wouldn't need such guidance.
- weinzierl 3y agoMy impression is the opposite, especially from when I used to work as a full-time Java dev. I often asked myself: Is there a place outside of GUI programming where inheritance is used in non-habitual and useful way? I can't think of many. More often than not you have a final class that you are supposed to use and the petrified hierarchy above that is of not much use.
- dan-robertson 3y agoPerhaps having some abstract class with a real and mock implementation inheriting from it, which can then be injected into your code for testing other components is one example.
- Gibbon1 3y agoI actually think with generics without type erasure and duck typing a language doesn't need inheritance. And when applications are built around passing data between services OOP tends to be less useful. (eggs are passed to the frying pan service and bread goes to the toaster service)
- otikik 3y agoInheritance is a local maxima. When the hierarchy of classes to consider is small, it often “seems to fit”. It allows the programmer to progress quickly and with low effort as a lot of the code sharing behavior is provided by the inheritance mechanism. Trouble often comes down the line. We keep adding classes, and soon we find that the hierarchy no longer is “shaped like a tree”. A soccer ball is a spherical object but also a sports equipment and a bouncing object, and there’s no way to organize those into branches. Our reality isn’t “tree-like”, except when we simplify it extensively.
- wwweston 3y agoHence traits.
- gentleman11 3y agoThat’s true of all knowledge organizational structures though. Even with composition, you end up with hierarchies that later you might turn on their heads, and also, those structures might end up being cyclical. Paradigm shifts happen and shake up even loosely organized into structures like human perception, and most people’s perceptions aren’t trees
- vegetablepotpie 3y agoThere’s a rule-of-thumb I’ve found about OOP where you want to use composition when extending data, and you want to use inheritance when extending behaviors. The zoo animal metaphor that you often see used in teaching inheritance (lions are animals, simba is a lion all animals have a length, but lions also have claws etc.) is a bad perspective and you should never use inheritance to model a problem like that in the real world. The only time I’ve used inheritance, has been on toy problems that didn’t need it. Not to say it doesn’t have its place but when I encounter it, it’s time consuming to peel back all the layers or deal with idioms that don’t quite fit a problem that a developer has enforced through inheritance.
- gwbas1c 3y ago> The only time I’ve used inheritance, has been on toy problems that didn’t need it See https://news.ycombinator.com/item?id=39999644 https://news.ycombinator.com/item?id=39999644 It's very useful when a base class provides an incomplete implementation, and the child class completes the implementation. Sometimes I use it when testing, where the child class alters some behavior in a way that mocking can't do correctly. In C# / Java, inheritance is used for setting up filters for exceptions
- cryptos 3y agoMy impression is that inheritance is in many common programming languages the easiest way to share code. Sharing code in another way would require some more thoughts and sometimes code, so the lazy programmer takes inheritance. Traits as a general concept would be very useful in many programming languages, but only some (like Scala) have proper support for it. In principle a Java interface with default methods (implemented methods) is also something like a trait, but very limited compared to traits in Scala. I have no statistics, but my impression is that inheritance is much less used in Scala, because the language has an easy to use alternative. Another example is Go, where structs with their associated methods can be embedded, what is exactly composition supported by the language. Since Go doesn't support inheritance, programmers obviously needs to use this approach, so you would never see inheritance in Go programs. So, my conclusion is that the usage of inheritance depends on what the language supports and how easy it is to use.
- ehnto 3y agoTraits and contracts or interfaces, but even still inheritance has it's merits. Being fast and easy is a benefit, and I find defensive programming in extensible systems can benefit from a foundation of expecting inheritance.
- josephg 3y agoWhat do you mean by this sort of defensiveness? Can you give an example?
- ehnto 3y agoFor example in a plugin system, I can say you must inherit from BasePlugin and in code I can enforce that, BasePlugin can then have some protected functions that I can disallow from being overridden. It's good for frameworks and extensible systems where your code needs to guarantee certain things will happen when running third party code.
- Supermancho 3y agoTraits are increasingly common. https://en.wikipedia.org/wiki/Trait_(computer_programming) https://en.wikipedia.org/wiki/Trait_(computer_programming)
- AndrewDucker 3y agoIt can be useful in the situation where you want to override functionality in a class without making the functions public. If you're composing a class out of multiple chunks of functionality then all of the bits you want to call need to be public. If you're sublcassing and overriding then the bits you're overriding can be internal, and not part of your public API.
- hkkwritesgmail 3y agoI find that it helps most if we make algorithms the centerpiece of the design and then use inheritance to facilitate coding and make everything succinct. The days of unwieldy inheritance structures coming out of big design patterns are clearly over.
- jonathanstrange 3y agoI prefer languages with full OOP, ideally with multiple dynamic dispatch. What I'm not fond of are languages like Java that overuse OOP design patterns, force you into them, or require lots of OOP boilerplate. If you have a class whose sole instance serves as a factory for other objects, you know you're on the wrong path. But I've never seen any coherent and sound arguments against OOP in general. Nobody forces you to use it, and, for example, it would be much easier to develop and deal with GUI frameworks in Go if it had classes with inheritance. As a rule of thumb, if for some reason your language drives you to emulate dynamic dispatch with explicit type switches on one or even multiple arguments, then it probably should have inheritance and dynamic method dispatch.
- mrkeen 3y ago> But I've never seen any coherent and sound arguments against OOP in general. How about arguments for OOP? Especially for things which can't be done better elsewhere.
- jonathanstrange 3y agoAs others have said, GUIs are the place where OOP shines. There are no things OOP does that can't be done elsewhere, what happens in practice is that programmers invent their own ad hoc object systems and start programming in an object-passing style (see e.g. Gtk).
- mrkeen 3y agoWhat is an object-passing style?
- jonathanstrange 3y agoWhatever it takes to emulate object-oriented programming in languages without objects and classes. Creating structs as objects and passing them as first arguments. But I also meant it to include using closures, function pointers, and type-switches to emulate dynamic dispatch, creating inheritance hierarchies by embedding higher classed object into structs in whatever way your language supports. Sometimes people also resort to code generation. Even explicit dynamic dispatch may be implemented, e.g. by storing class hierarchies explicitly. The hallmark of it is that "objects" are passed as first arguments to functions in languages that don't support syntactic sugar for methods.
- poisonborz 3y agoMy take is that after something becomes sufficiently popular and mainstream, for a lot of people who want to be seen as innovative, the only way is to lash out against it. See all the articles on how hellishly bad agile, php or javascript is. A lot of criticism is valid of course, but that is irrelevant in the larger scheme of things - there is a reason they became what they are today.
- EVa5I7bHFq9mnYK 3y agoThere is a lot of things in the world today that are very bad and very popular, not the reason to stop hating them.
- EPWN3D 3y agoIt seems like protocols solve 90% of the problems that inheritance does, but with 10% of the headaches. Instead of trying to ensure that two types can both be passed to a function that only knows about the parent type, just have a parameter that says "Whatever's passed has to conform to this, I don't care what it is otherwise."
- vram22 3y agoCode example?
- layer8 3y ago“Protocols” is Apple-specific (or Objective-C/Swift-specific) terminology. They correspond to types 1 and 2 of inheritance that TFA mentions.
- david2ndaccount 3y agoPython also has protocols: https://typing.readthedocs.io/en/latest/spec/protocol.html#protocols https://typing.readthedocs.io/en/latest/spec/protocol.html#p...
- eyelidlessness 3y agoProtocols as a term have other prior art. Which shouldn’t be surprising because a protocol is also descriptive of interface boundaries between software products (such as, but not limited to, network protocols).
- layer8 3y agoThe term is unintuitive to me in that usage, because to me a protocol implies an exchange or a sequence of steps between two or more parties, such as in network or cryptographic protocol or a diplomatic protocol. Interfaces, however, only specify one endpoint of an interchange, in an inherently asymmetric way, and they primarily specify operation signatures, where there are often few constraints on the possible sequence of operations. An interface can specify or be part of a protocol as a special case, but to me the term doesn’t match the general case.
- seanwilson 3y agoNobody calls it this, but cascading styles in CSS is just like inheritance and I think should be avoided for all the same reasons. I feel it's a big part of why CSS at scale becomes unmaintainable. There isn't even a built-in way to compose two classes together if you want to avoid cascading/inheritance. It looks like composition over inheritance has caught on as the better default in other languages, but in the CSS world people still cling to the cascade as a best practice for some reason.
- ethanbond 3y agoObligatory: Tailwind
- seanwilson 3y agoI tried to avoid mentioning that, but Tailwind is showcasing how to write CSS using composition from inheritance. But it can't overcome the knee-jerk reaction from many CSS developers that don't see the problems that comes with CSS cascade/inheritance. I think this is mostly because leaning on cascading rules is seen as a best practice and most aren't going to question best practices much.
- Fauntleroy 3y agoTailwind is not a panacea—it has its own problems which withhold adoption, "kneejerk reactions" notwithstanding.
- danielvaughn 3y agoI'll never understand why a lot of CSS devs are opposed to it. I can understand the initial gut reaction, but I've been writing CSS for 14 years and I love Tailwind. The cascade is a wonderful idea that has unfortunately not played out well in practice. Anyone who thinks it's just a skill issue is deluding themselves. In all my years, I've never seen CSS that (a) leverages the cascade, and (b) scales elegantly. It all breaks down past a certain point. Tailwind has it's flaws, but it doesn't have the scaling problem.
- dboreham 3y agoIt's not bad. People like to feel smug by saying OO is bad and hence inheritance, meanwhile using some construct in their FP that is really the same thing.
- chowells 3y agoCan you explain how any construct in FP conflates subtyping and code sharing the way OO inheritance does?
- tossandthrow 3y agoyou seem to be misunderstanding different terms. You can do OO without inhertitence. inheritance is the worst as you grip with you types. just use strategy patterns instead.
- deleted 3y ago[deleted]
- deleted 3y ago[deleted]
- FrustratedMonky 3y agoOh, darling, strap in because the Hacker News catwalk is serving us a spicy mix of opinions on inheritance in programming! First up, we've got the crowd who treats CSS like it’s the ugly stepchild of inheritance, preaching the gospel of "composition over inheritance" as if it's the latest fashion trend. Then there's the old guard, clutching their pearls and insisting that inheritance isn't the problem—it's just misunderstood, like a moody teenager. Cue the functional programming aficionados, sashaying in with their "functions over classes" mantra, ready to throw shade at OOP's entire existence. And let’s not forget the star of the show, the claim that inheritance is the VIP at the GUI and game development party, although some party poopers argue that the cool kids moved on to ECS systems ages ago. Meanwhile, the language innovators are flaunting their Kotlin and Swift ensembles, dripping in modern features that promise to make inheritance feel so last season. In the midst of this fashion war, there's a heated debate on whether we should be dressing our newbie programmers in OOP gowns or functional frocks from day one. And, honey, let’s not even get started on the industry’s trend chasers, who once hailed OOP as the next big thing, only to ghost it faster than you can say "blockchain." In the end, it’s clear that in the world of programming, inheritance is either a timeless classic or a faux pas waiting to happen, depending on who you ask.
- keiferkif 3y agoIt's funny how easy it is to spot a ChatGPT reply
- FrustratedMonky 3y agoDo you think it is because it is too verbose? Long winded? A human wouldn't bother to write that much?
- davidalayachew 3y agoFor me, it was taking the joke too far. The catwalk comment intro was actually really good, but then it just kept running with it the whole way through. That is not in and of itself a sin, but unless there's a good reason to do so, it dries quickly. You need something to keep it flowing, otherwise it just feels like you really needed to write 8 flamboyant lines mocking HN about inheritance with catwalk references stuck in for all of the lines. Feels forced, almost like you are trying to meet a quota. Which is what makes it feel AI-ish, to me at least. Definitely one of the better ones though, very impressive. AI really is great at making some very impressive things. But since AI doesn't "get it", a lot of what it produces just feels off or misses the point.
- constantcrying 3y agoI think it is pretty clear that the reason inheritance is so prevalent is because it is a superficially good sounding idea, which again and again is being put in front of people. I am sure that many, many people have had the experience of a professor tell them a nice sounding story about purely hierarchical data and went on thinking that this is the way data should be modeled. Thankfully I believe that nowadays many people have understood what works and what doesn't work in OOP and we can actually try to get away from that model of data.
- bartvk 3y agoI don't know about other colleges, but when I teach my students inheritance, I tell them right away that it's a pretty bad idea. And that they will learn better ways in the next semester.
- constantcrying 3y agoI can't speak for what has happened since I left university, but when I was a student there you got to here about cars, who are also vehicles, dogs, who are also animals and similar stuff. Which has "limited" applicability of how inheritance works in real software systems.
- CyberDildonics 3y agoThis is exactly what I've seen and I think a lot came from java and C++ before it. Ironically C++ is a fantastic language now to use now while avoiding inheritance. In the late 90s, 2000s and maybe even now, people see inheritance and think that it makes sense. They make the vehicle, the car, the sedan and then get stuck, when all they really needed was an x and y point and a velocity.
- ben7799 3y agoI love the way people take their narrow range of experience and over generalize it to everything. Bonus points for having a blog or maybe a patreon to tell everyone how it really is. We have a million flavors of the month but in the end it doesn't really matter. Pick a flavor and there will be teams that are successful and teams that are not. No silver bullet will make the non-successful teams magically turn into successful ones. "I/We failed with <insert language feature>" never generalizes to "<insert language feature>" is bad. Then the community constantly sits there and acts like a bunch of geniuses because some programming pattern has been discovered.. except it's 50 years old.
- vram22 3y agoHa ha, yes, happens all the time. In this case, published in a book 30 years ago, at least: https://news.ycombinator.com/item?id=40005520 https://news.ycombinator.com/item?id=40005520
- reactordev 3y agoAhhh good 'ol XP/Gang of 4. Never ceases to be referenced. (for good reason)
- ben7799 3y agoPretty sure it was in Effective Java and Design Patterns in Java 20+ years ago as well since Java is the language that gets picked on so frequently for this stuff.
- fuzztester 3y agoHe he. Java and patterns, Java and frameworks (mumble Spring mumble IoC mumble dependency injection), Java and factories and builders, mumble <buzzword> ...
- ruszki 3y ago> "I/We failed with <insert language feature>" never generalizes to "<insert language feature>" is bad. I thought the same for decades, then I met Groovy and Grails. Of course, it’s not bad in an absolute sense (IMHO that doesn’t make any sense), but when some problem can be found only at runtime, when any proper compiler would catch that compile time, it’s hard to argue that it’s a good direction. Especially when TypeScript made it quite obvious what’s possible only with simple type checking.
- d_burfoot 3y agoIt seems like there's a huge industry of people critiquing Java and OOP but misdiagnosing the problems as technical rather than sociological. The immense pain and suffering that is related to Java and its ecosystem is because of the terrible organizational conditions that tend to co-occur with usage of Java. Big slow companies, long boring meetings, arguments about design patterns, "architects" who haven't written code in ten years, etc etc. Java correlates with, but does not cause, those conditions. Another way of saying it: if Java was good enough for Nutch to use to write Minecraft, it's good enough for the vast majority of business purposes.
- ldjkfkdsjnv 3y agoJava complexity is also an artifact of Java being used in large complex systems. People are really complaining about how hard programming is. There are many types of large scale systems where I would only code it using the JVM, everything else is a nightmare. Big tech companies largely feel the same
- ben7799 3y agoSo many of these articles are written by people with no CS background who then had a couple stints short stints programming and then overnight pivoted to being expert consultants. They don't really have experience building or maintaining large complex systems. I am really curious who hires them. Most of them will have significantly less experience than the senior members of a successful team, and often less relevant education.
- throwaway5959 3y agoNo one is more confident in their abilities than a boot camp grad.
- tootie 3y agoThere is an all-time great quote from Bjarne Stroustroup when asked about how many people hated C++ > There are two types of programming languages: The ones people complain about and the ones nobody uses.
- not2b 3y agoI only use inheritance for what the author calls ontological inheritance. For example, I find it useful to represent expressions and statements. I prefer composition if I'm trying to reuse data structures.
- feoren 3y agoBut ontological inheritance is the worst kind. Because your ontology is wrong. Similar things are kinda different. Different things are kinda similar. Your ontology is based on the linguistic remnants of taxonomic garbage from hundreds of years ago. There's no shelf.
- not2b 3y agoNot the way I use it, which is very limited. If the similar things are kinda different I use something else, as well as design things to expose the differences where required.
- arcticbull 3y agoBoring answer: Same reason we do a lot of stupid things, haha. “If C is so unsafe and C++ is so unwieldy why does anyone use them?” Because that’s what they learned, or because that's how the codebase is structured when you get there. When all you have is a bike you’re going to ride that shit everywhere. You rarely if ever get the chance to start from scratch at work (where most code is written) and your job is to follow the pre-set pattern most of the time unless it becomes completely untenable. Sure, inheritance is worse in pretty much every way, but if everyone you're hiring does it that way, your codebase is built that way, etc, you're going to make the best of it because that's your job.
- BatFastard 3y agoMain reason I use c++ is because it has the most mature libraries. I gave Rust a try, and lots of nice things about it. But many of its libraries are not very mature.
- josephg 3y agoWhat libraries do you miss from C++ that you can’t find in rust? ML is the big piece for me - there’s no mature equivalent to CUDA or PyTorch.
- BatFastard 3y agoAny kind of usable GUI library. Plus I am doing 3d graphics, and rust is not there yet.
- bluGill 3y agoAll the company specific libraries we have debugged of the past 30 years.
- layer8 3y agoIt’s only implementation inheritance that is questionable, and the reason is that it leads to mutual dependencies on implementation details between superclass and subclass. Reasoning about correctness becomes difficult, and the superclass is very constrained in what implementation details it can change without potentially breaking some subclass. Bertrand Meyer’s open-closed principle can be read that way: The superclass is closed to modifications. But that goes against the principle of decoupling interface from implementation: The superclass interface is now stuck with the existing superclass implementation.
- nforgerit 3y agoWhen I learned programming as a teenager, I was using one of the C++ classics which, of course, taught OOP and Inheritance as a magic silver bullet. When I then finished the book and its examples, I wanted to do my own exercises and went through about 1-2 very painful years trying to model my things using Inheritance and totally blamed myself for being too stupid to do software development. Only then I started searching and finding essays and blog posts of people criticizing OOP and esp. Inheritance for exactly the things I was struggling with. This felt like a relief! My question: Why is Inheritance still taught as a silver bullet? I'm seeing university courses using Inheritance both for Domain and infrastructural code reuse at the same time. Esp. in Germany I see a lot of stupid 90s alike "programming" courses and, no shit, according code bases. It's as if they were living in a gigantic bubble.
- jonnycomputer 3y agoMy situation is different than a lot of others--I work mostly with code-bases I 100% control--but I just don't use inheritance all that often, and if I do, it's never deeper than one or two levels. Now, I do most of my programming in Python and in JavaScript, and I do like using python's ABCs to specify interfaces for interfaces that I expect to be implemented, but that's mostly the end of it.
- ffsm8 3y agoI remember the original reason for composition over inheritance to be very specific to Java: you can only inherit one class, but compose your class from multiple interfaces, each of which can have default implemented methods. In python you've got polymorphism, mixins etc, so the reason for this suggestion doesn't really apply. Django heavily uses inheritance for the controllers for example - and it works very well.
- jonnycomputer 3y agoI don't really use mixins either, unless it's required by the libraries I'm using. Partly this is habit on my part (and I came to Python from Java). But there is a lot of functionality in python I tend to make only light use of. Like using decorators. I understand them, but they feel like magic, and imo, make code harder to understand if used too much. Same thing with inheritance and multiple inheritance.
- turnsout 3y agoI stopped using inheritance when Swift protocols and TypeScript interfaces came along. Although it sometimes means you need to do nasty things with generics, it beats the fragility of inheritance. With ObjC, I eventually came to hate updating superclasses, because I knew it would lead to unexpected behavior in subclasses.
- tootie 3y agoInheritance on interfaces is fantastic. Inheritance on implementations is fraught with peril. Java developers figured this out like 10 years ago.
- josephg 3y agoI had the same experience. I’m a few years into using rust now - which doesn’t implement inheritance at all and I don’t miss it. In typescript I barely use classes at all. I prefer an interface paired with a constructor function. I think it’s almost always better to first think of data as a struct, with associated code that acts on that struct. As the old saying goes, show me your data structures and not your code, and you don’t need to show me your code.
- zozbot234 3y agoRust implements interface inheritance out of the box via traits, but you can also replicate something very much like implementation inheritance via phantom types/the typestate pattern. It just finds very little use in general, because it's readily apparent just how clunky that whole arrangement is.
- pianoben 3y agoDespite misgivings about the language as a whole, I think that Go does interfaces extremely well, and made the right choice to discard "implementation inheritance". That said, in my time with obj-c I never really encountered the problem of fragile base-classes - protocols and categories gave us just about everything we needed. (the foundation classes use implementation inheritance extensively, but we don't typically modify those too often :) )
- slaymaker1907 3y agoFor a really neat implementation of OOP and inheritance, I would recommend checking out Racket's class system (not to be confused with generics). It mostly solves the multiple inheritance problem by mixins and also has a great interface system.
- shawn_w 3y agoI find Racket's OO stuff to be awkward and cumbersome to use, and rarely a good option. One of these days I'm going to port a CLOS-inspired Scheme OO system like Guile's GOOPS to Racket...
- caseysoftware 3y agoThe key is "prefer composition to inheritance" and dates back to Gang of Four. The word "prefer" is critical to understand. It just means "usually choose A over B" not "B is never the right answer." Unfortunately, since we - as an industry - like hard and fast rules, we move towards that second explanation and act like inheritance never makes sense. Like any tool, there's a time and place where it is the best tool, other times where it's a reasonable tool, and other times it's a terrible tool. Therefore "everyone uses it" because either a) it's a reasonable approach or b) the developer doesn't know of or can't use a better one. We should work on fixing b) instead of denying a) exists.
- cogman10 3y agoI 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.
- fire_lake 3y agoHmmm. I have exactly zero occurrences of implementation inheritance in my code base.
- feoren 3y agoExactly. "Why does everyone use it?" is a false premise. I don't use it.
- quantified 3y agoMeyer also thought (for a while) that inheritance was more powerful than genericity. Later he admitted his "proof" of that was weak.
- deleted 3y ago[deleted]
- lamontcg 3y agoInheritance is really just a public interface, a private interface and automatic delegation of those interfaces to the base class. The problems with inheritance are: - people shove code in the base class to dedup it without thinking about design - people add public and protected methods without thinking about interface design - the names of the base class and the public and protected interfaces are exactly the same thing The last point is that if you have a FooBase class which is public then you can wind up with a bunch of List<FooBase> containers that are coupled to the base class and external methods that take FooBase parameters along with a bunch of concrete instances which are dependent upon the FooBase class protected interface and code implementation. This creates the brittle base class problem. If instead you had an IFoo interface and only ever used List<IFoo> instead of FooBase anywhere then you could always define FooBasev2 which implemented IFoo as well and FooBase and FooBasev2 can coexist in your codebase without having to break any external consumers (open-closed principle in practice). If base classes were only allowed to be used in inheritance and couldn't be parameters, generics, etc then that would force users to create base classes and public interfaces in pairs and would decouple their names and by writing the public interface down as its own thing developers would be more likely to focus on that design. And really inheritance is just syntax sugar around having a component (the base class) which has automatic delegation of of the public interface and protected interface to it in the inheriting object, with so little typing that it becomes easy to not think about what you're doing -- and while reusing the same name for three different things and creating tight coupling, and you can't dependency inject different baseclasses at runtime. If languages had better terse syntax for delegation then composition would get a lot easier to use like inheritance is (which is something that Go oddly enough gets more-or-less correct, dunno why they didn't mandate piles of boilerplate for delegation like they did for error checking).
- sfn42 3y agoIt's worth mentioning regarding the IFoo example that in a single project you can always just find/replace FooBase with IFoo or even FooBaseV2. I feel like a lot of people blow this up like it's so e huge problem when in reality this is a pretty trivial refactor.
- 3y ago
- m463 3y agoI found multiple inheritance kind of fun when I was first exploring python. Then, madness...
- wakawaka28 3y agoBecause it's not actually so bad ;)
- GendingMachine 3y agoAs a programmer with likely less experience than most of these commenters, the main question that always feels under addressed in these kinds of posts is that of code deduplication. Most specifically, often when encountering a situation in which I have two slightly different classes/types that need to be polymorphic with each other, all the standard non-inheritance based approaches seem to require a lot of outright identical code implementing a shared interface, a bunch of boilerplate composition proxy methods that just point to methods in a composed class, or weird and obscure language features that never really feel like the "intended" approach. Typescript especially seems to demand really awkward and obtuse implementations of basic mixins or abstract classes and the like, I always feel like I'm missing the more "intended" approach whenever I try to share code between similar classes. Often discussions around this are awash with talk of traits and delegates and other cool features that seem to only exist in a handful of languages, and never the ones I happen to be required to use at that given moment.
- contravariant 3y agoIt's always possible to 'deconstruct' objects with methods into just dumb structs with functions acting on them. This is the direction Julia takes for instance. One advantage of this is that it exposes that 'inheriting' a method is really just applying the same base function to all classes that support a particular interface. You can override a method by specialising the function for a subinterface. This interface can just be a marker that says 'this struct has these fields, and semantically is a 'IMyInterface'). Of course object oriented programming languages tend to encourage private and protected properties and such, which force all of this to take place inside the class. At first I though that that was the best way to avoid a mess, but it prevents you from doing this, possibly leading to more code duplication. And after some more experience with python there's something to be said for python's approach of just using name conventions to point out when to be careful.
- chuckadams 3y agoTypescript is best when you don't use `class` or `this` at all, but instead return object literals of static closures. In other words, more like this: function makeCounter(initial = 0) { let count = initial; function get() { return count } function inc() { count += 1 } function dec() { count -= 1 } return {get, inc, dec} } // works just like a normal object const ctr1 = makeCounter(); ctr1.inc() // but this works too. try doing this with a class instance. const {get, inc} = makeCounter() inc() This is the typical style of most VueJS code (though it would replace count with a ref and probably call the function `useCounter`). TypeScript is smart enough that it can infer interfaces and even classes from such literals should you go that way, full support for substitutability checking and all.
- jokethrowaway 3y agoI look mostly at JS and some Rust codebases and almost nobody uses inheritance! I believe it doesn't make any sense, you can use different patterns to have the benefits without creating a complicated type hierarchy where state and functions are mixed across different files. Whenever I would use inheritance I use the builder pattern.
- revskill 3y agoUse inheritance as vomposition with parent ?
- pyuser583 3y agoA lot of it goes back to Java. Java has .class files, not .instance files.
- throwiforgtnlzy 3y agoWhile I don't use the ecosystem, I like the way Go approaches it with interfaces by being minimal, expressive, and flexible. It something works like something else according to a minimal specification, it's the same. There's no sealing off things or top-down mandates that require changes to other people's code. Rust traits aren't exactly the same because they require changes to everyone's code to follow an exact model that cannot be retrofitted to existing code without additional work. One downside (it may not be the case today) with the Go model is requiring a type to fulfill one or more interfaces requires additional hoops such as no-op init() interface assertions.
- reify 3y agoIf.......... Why......... Everyone.... click click click click click click click click click click click click click click click click click click click click click bait
- hnycuc23 3y agoWe don't use inheritance anymore. In all the years building small and hughe software solutions with small and larger teams I've never experience any real scaling, maintenance or resilience benefit. Even for documentation it is more a pain for new devs onboarded than an advance. Patterns,DRY, DI, IOC, MS, ES, IO are best to build with these scopes in head.
- silent_cal 3y agoNerds make things complicated, no matter what. It is their fundamental impulse that cannot be tamed, like the scorpion who must sting the frog that carries it across the river.
- lucasyvas 3y agoThere is no valid use case I've ever seen where inheritance was better than composition (that does not mean a use case does not exist, but evidence is mounting against it). I would say it's used because every language made a poor decision by building that in as the way to encapsulate reusable logic. i.e., a mistake. Some APIs may expose themselves as requiring you extend a base class, and in that case you might as well do what the library author said. But overall? Yes, some things can work well with inheritance (eg. classical UI frameworks), but these are often equally good with a composition based API. One job I was at I added a lint rule to flag `extends` as an error. You would need to add a comment to justify why you were using inheritance - it went over more favourably than I expected. I think I would suggest this for any new projects using a language that supports inheritance - discourage it with automation, but allow really good reasoning to prevail in review. It is the worse solution at least 99% of the time because it requires you predict a future that is painful to change.
- TheBigSalad 3y agoComposition implies a level of complexity. There may be a near infinite number of permutations of things that can be combined. If in reality there are only a fixed set of variations, inheritance may model your system in a tighter way. The benefit can be simplicity.
- Capricorn2481 3y ago> Composition implies a level of complexity How?
- TheBigSalad 2y agoInheritance is more limited in what you can do and implies a finite set of behavior states. With composition you have a system that can, in theory, have an untestable amount of permutations of behavior strung together.
- Capricorn2481 2y ago
- xorvoid 3y agoDepends what language you're using and what polymorphism features it has... C - You don't use inheritance because it literal has none, but you can handroll dynamic dispatch (interface style Abstract Base Classes) Rust - Also doesn't have it and people seem perfectly happy with generics, sum-types, and `dyn Trait`
- da39a3ee 3y agoOne thing that bothers me is when people refer to languages like Go and Rust as "not object-oriented". Of course they are object oriented; Go and Rust programs consist of struct definitions to which you associate methods, giving rise to individually instantiatable objects with custom behavior, and you use these objects to model entities in your problem domain. This is object-orientation. Where they differ from OO languages such as Java and Python is of course that they avoid traditional inheritance, offering more restricted inheritance-adjacent features (typeclasses/traits/interfaces and struct embedding). Like many others I find Go disappointing in many regards. (Although Go's channels, goroutines, select, and implicit interfaces are beautiful). One disappointment is the way that methods are not indented or nested within the struct definition/interface that they are defined on. This is, IMO, a silly fig-leaf that is attempting to make the language look "not OO" in a superficial manner, but all it ends up doing is making it hard to see where your implementation of one interface ends and another starts.
- sporkland 3y agoIn my mind, the more generic rule underneath these rules is you want to be able to comprehend what a unit is doing without jumping between a massive number or files or contexts. Inheritance hierarchies with shared state and overrides means you need to understand multiple files and their interactions vs interfaces and composition tend to allow for fairly understandable units with clear interactions. The big problem these deep inheritance hierarchies cause is mediocre programmers often think they're writing good code (often high focus on maximal reuse) but the interfaces and clarity are terrible.
- beryilma 3y agoI don't think that inheritance is bad per se. AbstractWindowButtonManagerFactory type of inheritance is bad. I think the reason is that people are not just good at constructing hierarchical relationships between abstract concepts. And once you construct such an elaborate hierarchy, it becomes very hard to correct things after the fact. So you end up with a mess as people add more functionality on top of an already ill-conceived structure.