[HN Gopher] If Inheritance is so bad, why does everyone use it?
       ___________________________________________________________________
        
       If Inheritance is so bad, why does everyone use it?
        
       Author : harperlee
       Score  : 124 points
       Date   : 2024-04-11 06:35 UTC (16 hours ago)
        
 (HTM) web link (buttondown.email)
 (TXT) w3m dump (buttondown.email)
        
       | signa11 wrote:
       | huh :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 wrote:
         | Yeah in C++ especially nowadays using compile-time traits is
         | much, much more idiomatic.
        
         | constantcrying wrote:
         | >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 wrote:
       | IMHO 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 wrote:
         | Inheritance doesn't work well for games, that's why so many
         | games take a component based approach like Unity, or full blown
         | ECS.
        
           | lewispollard wrote:
           | Well, 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 wrote:
             | Unless it'a role playing game.
        
           | professoretc wrote:
           | The _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.
        
         | fxd123 wrote:
         | That's strange because I was about to comment the exact
         | opposite. ECS is for games, inheritance is for almost
         | everything else
        
           | ipaddr wrote:
           | For 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 wrote:
         | This 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 wrote:
         | Especially 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 wrote:
         | and it became popular at just about the same time as GUI
         | programming, coincidence?
        
         | cess11 wrote:
         | The 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 wrote:
       | Lots of design patterns have their uses, but can suffer when
       | applied too overzealously.
        
         | JauntyHatAngle wrote:
         | Like almost anything in IT, if you are good at using a hammer,
         | you treat everything like a nail.
        
           | chii wrote:
           | if 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 wrote:
       | Do they? I rarely see people using it directly and I think this
       | is fine
        
         | angiosperm wrote:
         | Most 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 wrote:
           | This 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 wrote:
           | java 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 wrote:
             | "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 wrote:
             | By "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.
        
         | brodo wrote:
         | Same here. For me, implementation inheritance is a code smell.
         | But I rarely see it.
        
       | KorematsuFredt wrote:
       | Here 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 wrote:
         | Pragmatists 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 wrote:
           | FP rather slows things down though.
        
             | pyrale wrote:
             | What specifically slows it down in your context?
        
               | mewpmewp2 wrote:
               | Doing 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.
        
               | pyrale wrote:
               | Would you say it's a retraining cost, or would you
               | imagine still having this issue many years from now,
               | assuming you keep working on it?
        
               | mewpmewp2 wrote:
               | It likely also depends on the person so I couldn't speak
               | on behalf of everyone. I have been exposed to it to some
               | extent, not the majority of work for over 5 years and it
               | still intuitively is challenging for me. So similar task
               | would take longer to code and existing code would take
               | longer to understand. Significantly more effort would go
               | to that.
               | 
               | I feel like I spend more time and energy on how to get
               | something done functionally rather than focusing on what
               | the correct business logic should be.
        
               | bedobi wrote:
               | lol it's the opposite? map, flatmap, fold etc are very
               | clearly defined operations with very clear use cases and
               | rules. loops are not, you can do whatever you want (often
               | mutating the underlying, rug pulling you every iteration)
               | 
               | not being familiar with fp doesn't mean it's objectively
               | worse
        
           | throw_m239339 wrote:
           | > Pragmatists used inheritance
           | 
           | Because at some point mainstream OO languages made
           | inheritance easy and composition harder, nothing more.
           | 
           | Composition with Java used to be verbose. Inheritance
           | declaration was simply a single keyword.
           | 
           | If Java had "mixins" from the start, people would have used
           | composition way more.
        
             | StressedDev wrote:
             | I saw a lot more composition than inheritance in C++ and
             | C#. I think inheritance and polymorphism have their place.
             | Like anything, they can be misused.
        
           | pyrale wrote:
           | > The Church of Purity now bangs on about Functional
           | Programming in the same way for the same reasons.
           | 
           | FP in the industry is a niche thing, so the two phenomena are
           | not really comparable.
        
         | blarg1 wrote:
         | Also a semi religion, I'm always seeing people say before OOP
         | everything was terrible, and everything good thing is due to
         | OOP (eg structs, methods, polymorphism)
        
         | bluetomcat wrote:
         | In the 1990s, it coincided with the proliferation of GUIs and
         | their respective programming interfaces. Most frameworks use
         | hierarchies like Object->View->Control->Button->ImageButton.
         | Then people decided that this is the way for modeling abstract
         | problems that don't have to deal with visual or real-world
         | entities whatsoever.
        
           | dsego wrote:
           | And you would get nice automatic completion when doing object
           | name, dot, and waiting for the IDE to list all possible
           | methods. This was exploratory programming, the copilot of its
           | time.
        
       | fardo wrote:
       | I 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 wrote:
       | Many 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 wrote:
       | OOP 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 wrote:
         | I 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 wrote:
           | But 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.
        
             | bl4ckm0r3 wrote:
             | I think you identified the selling point of OOP, it's easy
             | to reason about it and logically it makes sense to human
             | because we do think in terms of objects (the user, the
             | order, the item etcetc) and responsibility segregation
             | (User->login(), Order->fulfil(), etc). To your example of
             | just launching intellij and using java (or ruby or
             | whatever) to build something, yeah it works and it works
             | fast and it's easy to add new features, and it's also easy
             | to end up with a >3k loc User class in python that no one
             | can touch (true story).
        
             | cess11 wrote:
             | I prefer to start with an interactive programming
             | environment, like a REPL, to keep the feedback loop tight
             | and code short. If I needed to talk for ten minutes about
             | templates and instances and how you need to have a little
             | meeting with yourself and do modeling I'd have lost their
             | interest right away. These words mean nothing to a newbie,
             | unless they're some kind of philosophy nerd so I can hook
             | into ancient greek ideas about Forms or something.
             | 
             | 'Here is a way to do simple math, here is a way to glue
             | text to text, now that has grown a bit, you can shorten it
             | for repetitions by giving it a name like this', and so on.
             | 
             | And to me, starting out functionally is easy. Data is
             | pretty much always a given, it's very rare in practice that
             | I first need to model speculatively and then generate data
             | ex nihilo unless I'm creating mocks. Usually there is a
             | data source, a file, a network API, whatever, and then I
             | start building against that. Small, simple things at first,
             | like mapping all the input to an output interface, then add
             | transformations and output for these, and so on.
             | 
             | In general I spend more time thinking about the shape of
             | data I already have than architecting code or inventing
             | models.
        
       | zelphirkalt wrote:
       | Some 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 wrote:
       | Nowadays I have rarely seen it if ever
        
       | cess11 wrote:
       | Smalltalk 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 wrote:
       | I 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."
        
         | gridspy wrote:
         | > Let's stay I have a base class A. Let's say I have 70 classes
         | that inherit from A.
         | 
         | The alternative case is that you have 70 classes that CONTAIN
         | A. You still only have to change A.
        
           | moshegramovsky wrote:
           | Yes, but in my example, in the 70 classes, all the ::Read and
           | ::Write methods must still say:                   a.Read( ...
           | )         a.Write( ... )
           | 
           | And depending on the use case and design, they also need
           | methods such as:                  A& GetA() { return a; );
           | const A& GetA() const { return a; }
        
         | planede wrote:
         | The answer is that you retain A as a pure interface and maybe
         | you add a convenience base below A that adds the data member,
         | then you inherit from from the convenience base for your 70
         | leaf classes. You can also use CRTP for the convenience base,
         | if it's appropriate. You can still avoid confusing interface
         | and implementation inheritance.
        
       | drewcoo wrote:
       | Not 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/
       | 
       | And not everyone uses inheritance.
       | 
       | Maybe you have another question, like "why would someone use
       | inheritance?"
        
       | peter-m80 wrote:
       | Speak for yourself, I don't use it.
        
       | bigstrat2003 wrote:
       | I 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 wrote:
         | Depends 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 wrote:
         | Have 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 wrote:
           | You 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 wrote:
             | I 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.
        
             | ben7799 wrote:
             | The person who wrote that would have made your life
             | miserable if they had architected it 5 other different ways
             | as well.
        
           | touisteur wrote:
           | Feels like that time my physics PhD student girlfriend asked
           | me "hey you're a programmer, right" and I _was_ one until I
           | discovered ROOT and suddenly I lost taste for life and
           | anything related to Computers.
        
             | esafak wrote:
             | Did you become a reclusive mathematician like Perelman or
             | Grothendieck??
        
           | kaba0 wrote:
           | Sure, but I have also seen C code where random structs with
           | function pointers are passed to god-knows-where, and I will
           | take inheritance over that any time of the day.
        
         | MattPalmer1086 wrote:
         | I have found that single inheritence can be useful but it is
         | very restrictive.
         | 
         | More than once, I have found an inheritance hierarchy that made
         | sense when first created no longer models the problem well. It
         | becomes hard to change it at that point.
         | 
         | Frequently I find I really want to mix in cross cutting
         | concerns across different hierarchies. This isn't really a
         | surprise; most problems do not naturally decompose to only a
         | single view of things.
         | 
         | I don't mind abstract base classes containing common
         | functionality, so it's useful there, but mix ins would be
         | better to be honest.
        
         | thiht wrote:
         | I think that's just what you're used to. Since I switched from
         | Java to Go 7 years ago, I don't think I've missed inheritance a
         | single time. I haven't needed to model anything with
         | inheritance once. There are definitely things I've missed from
         | Java, but not inheritance.
        
           | jerf wrote:
           | A lot of people who only know inheritance and can't
           | understand why a lot of us slag on it so much don't
           | understand how inheritance conflates implementation and
           | interface. Polymorphism based on interface is a fundamental
           | tool of programming. The way inheritance also drags along an
           | implementation turns out not to be, and to probably be more
           | trouble than it's worth. There are other ways of bringing
           | along implementation, including the basic function, and those
           | work better.
           | 
           | When inheritance is your only tool for interface, I
           | absolutely agree that it looks fundamental, but the
           | fundamentalness is coming from the interface side. Once you
           | separate out interface from implementation, it turns out I
           | have little to no use for the implementation portion.
           | 
           | I have also been programming in Go for a long time now. I
           | sketched out an inheritance system once for an exercise. It's
           | doable, though it's a terrible pain in the ass to use. I've
           | kept it in my back pocket in case it is ever the right
           | solution to a problem even so... but it never has been. And
           | that's not because of any irrational hate for the pattern
           | itself. I've imported other foreign patterns sometimes; I've
           | got a few sum types, even though Go isn't very good at it,
           | because it was still the best solution, and I've got a couple
           | of bits of code that are basically dynamically typed, again
           | because it was the best solution, so I'm not against
           | importing a foreign paradigm if it is the correct local
           | solution. Inheritance just... isn't that useful. The other
           | tools that are available are sufficient, and not just
           | begrudgingly sufficient or "I'm insisting they're sufficient
           | to win the argument even though I know they really aren't"...
           | they really are sufficient. Functions are powerful things, as
           | the functional programming community (it's right there in the
           | name) well knows.
        
         | constantcrying wrote:
         | I have never really seen it work. To me it seems it can work if
         | and only if you are doing serious waterfall projects, with a
         | tightly bounded scope.
         | 
         | If you have to model your data exactly once I believe it can
         | work. But if you are doing any kind of agile, or even somewhat
         | flexible waterfall, you will find out that at some point none
         | of your data models work.
        
         | BeetleB wrote:
         | > I don't think inheritance is bad at all. It is very often the
         | easiest way by far to model a problem.
         | 
         | I think the point of the article is that in some (very popular)
         | languages, inheritance comes with a lot of baggage - precisely
         | because classes in those languages support 3 different use
         | cases. It's not saying that your usage is bad, but that your
         | language didn't provide you with the best tool(s).
         | 
         | A lot of how people use classes can be solved via ADTs - which
         | are much simpler to grok.
         | 
         | A lot of how people use classes can be solved via
         | namespaces/modules, but not all languages have good support for
         | them - so they use classes.
         | 
         | Etc.
        
       | pvdoom wrote:
       | IDK, 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 wrote:
       | Kotlin 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 wrote:
         | Same with Swift
        
         | pdpi wrote:
         | One 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 wrote:
           | Sane defaults are the bomb. Defused bomb.
        
       | v1sea wrote:
       | The 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
       | 
       | [1] https://wiki.vg/Entity_metadata#Donkey
       | 
       | [2] https://wiki.vg/Entity_metadata#Blaze
        
         | Sakos wrote:
         | Both of those look like perfect cases for composition instead
         | of inheritance.
        
           | coffeebeqn wrote:
           | Anyone not using components for game objects is insane. How
           | do you add a fire breathing horse without that
        
             | feoren wrote:
             | Yes, 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?
        
         | monknomo wrote:
         | why is monster vs ageable mob a good cleavage? This is odd
        
       | NotGMan wrote:
       | Black 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 wrote:
       | I quite like inheritance personally, assuming that it's done
       | reasonably well and isn't a confusing mess.
        
       | captainmuon wrote:
       | I 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 wrote:
         | Polymorphism 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 wrote:
           | > 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);
        
           | vidarh wrote:
           | Because syntactic sugar and abstractions matter. You can do
           | everything in assembly too, yet we prefer something higher
           | level.
        
           | michaelrpeskin wrote:
           | > what is the point of creating a language where everything
           | is an object?
           | 
           | I think that's the ultimate culprit in everyone hating
           | inheritance. If it weren't for Java, I think we'd all have a
           | healthier view of OO in general.
           | 
           | I learned OO with C++ (pre C++-11), and now I work at a Java
           | shop, but I'm luck that I get to write R&D code in whatever I
           | need to, and I spend most of my time in Python.
           | 
           | In C++ and Python, you get to pick the best tool for the job.
           | If I just need a simple function, I use it. If I need run-
           | time polymorphism, I can use it. If I need duck-typing I can
           | do it (in Python).
           | 
           | Without the need for strict rules (always/never do
           | inheritance) I can pick what makes the best (fastest? most
           | readable? most maintainable? most extensible? - It depends on
           | context) code for the job.
           | 
           | Related to TFA, I rarely use inheritance because it doesn't
           | make sense (unless you shoehorn it in like everyone in the
           | threat is complaining about). But in the cases where it
           | really does work (there really is a "is a" relation), then it
           | does make life easier and it is the right thing.
           | 
           | Context matters, and human judgement and experience is often
           | better than rules.
        
             | Kranar wrote:
             | C++ templates are a form of duck typing as well, and
             | combined with type erasure give you a lot of the benefits
             | of OO without the downsides.
        
               | michaelrpeskin wrote:
               | Yes, you're right. I've done that in high-performance
               | code where I couldn't afford the double function call of
               | a virtual function. I forgot about that.
        
         | ano-ther wrote:
         | Yes. I have a framework for an embedded system that uses
         | various types of sensors. When changing a sensor, instead of
         | rewriting the polling loop for every new case, I can keep
         | looping through 'sensor[i]->sampleNow()' and add the specifics
         | in a class inheriting from SensorClass.
        
           | touisteur wrote:
           | Interface inheritance could do the trick then? Maybe function
           | pointers even.
        
         | mrkeen wrote:
         | > 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.
         | 
         | The runtime part is what I dislike. If I have a fruit which is
         | an apple or a banana, I can't pass that to a method expecting
         | an apple or banana. It can only be passed as a fruit.
         | 
         | > 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.
         | 
         | This is common OO wisdom that I strongly disagree with. For
         | example, in my program I have a few types (Application,
         | Abstraction, Variable, etc.), and a lot of transformations to
         | perform on those types (TypeCheck, AnfConvert, ClosureConvert,
         | LambdaLift, etc.).
         | 
         | I prefer to have all the type-checking code inside the
         | TypeCheck module, and all the closure-converting code inside
         | the ClosureConvert module. I'd take the "huge if-else"
         | statements inside TypeCheck rather than scatter typechecking
         | across all my datatypes.
        
           | rtz121 wrote:
           | > If I have a fruit which is an apple or a banana, I can't
           | pass that to a method expecting an apple or banana. It can
           | only be passed as a fruit.
           | 
           | You can by overriding the method on apple or banana. If your
           | method is on some other object, then yes, you cannot do this
           | unless your programming language supports multiple dispatch.
        
           | zbentley wrote:
           | > I prefer to have all the type-checking code inside the
           | TypeCheck module
           | 
           | There are ways to have your cake and eat it too, here, at
           | least in some languages and patterns.
           | 
           | For example, in Go you could define "CheckType" as part of
           | the interface contract, but group all implementors' versions
           | of that method in the same file, calling out to nearby
           | private helper functions for common logic.
           | 
           | Ruby's open classes and Rust's multiple "impl" blocks can
           | also achieve similar behavior.
           | 
           | And yeah, sure, some folks will respond with "but go isn't
           | OO", but that's silly. Modelling polymorphic behavior across
           | different data-bearing objects which can be addressed as
           | implementations of the same abstract type quacks, very
           | loudly, like a duck in this case.
        
           | kaba0 wrote:
           | > The runtime part is what I dislike. If I have a fruit which
           | is an apple or a banana, I can't pass that to a method
           | expecting an apple or banana. It can only be passed as a
           | fruit.
           | 
           | Heh? An apple _is_ a fruit, you can pass it to any place
           | expecting the former. Like, this is Liskov's substitution's
           | one half.
           | 
           | With generics, you can be even more specific (co/in/contra-
           | variance).
        
             | cornstalks wrote:
             | I think GP is talking about something like this:
             | Fruit* fruit = new Apple();         ConsumeApple(fruit);
             | // Doesn't work; requires Apple*              fruit = new
             | Banana();         ConsumeBanana(fruit);  // Doesn't work;
             | requires Banana*              ConsumeFruit(fruit);  //
             | Okay, function signature is void(Fruit*)
        
         | parpfish wrote:
         | if i were writing an intro to programming book, i would
         | introduce OO as a means of building encapsulation. i'd only get
         | into inheritance in later chapters.
        
           | gentleman11 wrote:
           | You can have encapsulation without oop. Polymorphism is the
           | real benefit of oop imho
        
             | gorjusborg wrote:
             | > Polymorphism is the real benefit of oop imho
             | 
             | It's pretty much the _definition_ of OOP.
             | 
             | The core feature of OOP is just bundling functions with the
             | state it processes.
             | 
             | When you bundle state and functions together, you can't
             | predict what calling the function will do without knowing
             | both the code and state.
             | 
             | You can say its 'the real benefit', I guess, but that feels
             | like circular reasoning. Its pretty much the definition of
             | what OOP _is_ , so calling it a benefit feels weird.
             | 
             | Unfortunately, designing systems as a collection of
             | distributed state machines tends to become maintenance
             | nightmare. Functions and data being separated tends to make
             | code better, even when working in so-called 'OOP'
             | languages.
        
         | eddd-ddde wrote:
         | Except inheritance is the premature optimisation of interfaces.
         | 
         | Inheritance forces you to define the world in terms of strict
         | tree hierarchies, which is very easy to get wrong. You may even
         | do a great job today, but tomorrow such properties don't hold
         | anymore.
         | 
         | Regular composition allows the same functionality without
         | making such strong assumptions on the data you are modelling.
        
           | sameoldtune wrote:
           | Arborescent vs rhizomatic approaches, to get philosophical.
        
             | 082349872349872 wrote:
             | Unfortunately Deleuze's _The Fold_ has little to say about
             | either awk or catamorphisms.
        
           | dragonwriter wrote:
           | > Inheritance forces you to define the world in terms of
           | strict tree hierarchies,
           | 
           | No, it doesn't.
           | 
           | Inheritance is the outcome of _deciding_ to model _some part_
           | of the problem space with a tree hierarchy (that potentially
           | intersects other such heirarchies). It doesn't force you to
           | do anything.
           | 
           | I suppose if there was a methodology which forced you, as the
           | only modeling method, to exclusively use single inheritance,
           | that would force you to do what you describe, but...that's
           | not inheritance, that's a bunch of weird things layered on
           | top of it.
        
         | sameoldtune wrote:
         | You're describing the strategy pattern, which is probably one
         | of the most practical coding design patterns. Ex: each chess AI
         | difficulty gets its own class, which all extend a common
         | interface.
        
           | krapht wrote:
           | I still find it funny that somehow the idea of runtime
           | function dispatch via function pointer is called the
           | "strategy" pattern.
        
             | sameoldtune wrote:
             | Hey I didn't name it!
        
         | VirusNewbie wrote:
         | >For me the main point is (runtime) polymorphism.
         | 
         | But you don't actually care about runtime polymorphism here.
         | You care about polymorphic behavior, which can be implemented
         | in a much more composable way with parametric polymorphism.
        
           | layer8 wrote:
           | You can't build a dynamic list of objects implementing the
           | same interface in different ways with parametric
           | polymorphism.
           | 
           | As another example, the Unix file interface ( _open()_ ,
           | _read()_ , _write()_ , _flush()_ , _close()_ ) etc. is an
           | example of runtime polymorphism, where the file descriptors
           | identify objects with different implementations (depending on
           | whether it's a regular file, a directory, a pipe, a symbolic
           | link, a device, and so on).
           | 
           | All operating systems and many libraries tend to follow this
           | pattern: You can create objects to which you receive a
           | handle, and then you perform various operations on the
           | objects by passing the respective handle to the respective
           | operation, and under the hood different objects will map to
           | different implementations of the operation.
        
             | VirusNewbie wrote:
             | >You can't build a dynamic list of objects implementing the
             | same interface in different ways with parametric
             | polymorphism.
             | 
             | Yes you can. that's the whole point of type classes.
        
               | layer8 wrote:
               | Not without runtime polymorphism. Parametric polymorphism
               | does not imply nor by itself implement runtime
               | polymorphism. I.e. C++ templates, or generics in other
               | languages, provide parametric polymorphism, but not
               | runtime polymorphism.
        
               | Kranar wrote:
               | Type classes are not parametric polymorphism.
        
         | dblohm7 wrote:
         | Unfortunately a lot of developers are conditioned so heavily to
         | believe that inheritance is intrinsically bad, that they
         | contort their code into an unreadable mess just to re-implement
         | something that could have been trivial to do with inheritance.
         | 
         | I'm not saying that I like it everywhere; IMHO it's a tool to
         | be used sparingly and not with deep hierarchies. But it's not
         | reasonable to avoid it 100% of the time because we're soiling
         | ourselves over the thought of possibly encountering the diamond
         | problem.
        
         | tombert wrote:
         | I feel like Clojure-style multimethods accomplish this better
         | than inheritance. I can simply write a dispatcher that
         | dispatches the correct function based on some kind of input.
         | 
         | This is evaluated at runtime, thus giving me the runtime
         | polymorphism, but doesn't make me muck with any kind of
         | taxonomy trees. I can also add my own methods that work with
         | the dispatcher, without having to modify the original code. I
         | don't feel like it's any less convenient than inheritance, and
         | it can be a lot more flexible. That said, I suspect it performs
         | worse, so pick your poison.
        
         | BeetleB wrote:
         | > I feel people don't understand ...
         | 
         | > For me ...
         | 
         | Sorry, but right off the bat this is just painting you as an
         | example of "There are N camps, and each camp declares the other
         | camp as wrong."
         | 
         | Yes, that's what inheritance is for _you_. For _others_ it is
         | something else. Why is _your_ way the one that  "correctly
         | understands" it?
         | 
         | The article itself covers this - that some languages have
         | lumped 3 different concepts into one that they call
         | inheritance, leading to the different camps and comments like
         | yours. Your camp is specifically mentioned:
         | 
         | > Abstract data type inheritance is about substitution: this
         | thing behaves in all the ways that thing does and has this
         | behaviour (this is the Liskov substitution principle)
        
         | CharlieDigital wrote:
         | I 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.
         | 
         | Agree 100%. It starts from the earliest programming course
         | where we just teach it all wrong; way to abstract (no pun
         | intended).
         | 
         | One point to add to yours: well executed OOP allows for
         | "structural" flow of control where the object hierarchy,
         | inheritance, events, and overrides allow for the "structure" of
         | the hierarchy to control the flow of logic.
         | 
         | I wrote two articles on this with concrete examples and use
         | cases:
         | 
         | https://medium.com/codex/structural-control-flow-with-object...
         | 
         | https://medium.com/@chrlschn/weve-been-teaching-object-orien...
        
         | quaunaut wrote:
         | If people get it wrong so regularly, what value is it providing
         | as a concept? These concepts are supposed to help us reach
         | something better, if you have to add 30 caveats to every part
         | of it, all it did was hide its own complexity from you, instead
         | of managing it for you.
        
       | datascienced wrote:
       | Inheritance 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!)
        
       | nertirs wrote:
       | I 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 wrote:
         | I'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 wrote:
       | My 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 wrote:
         | Perhaps 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 wrote:
         | I 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 wrote:
       | Inheritance 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 wrote:
         | Hence traits.
        
         | gentleman11 wrote:
         | That'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
        
       | cryptos wrote:
       | My 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 wrote:
         | Traits 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 wrote:
           | What do you mean by this sort of defensiveness? Can you give
           | an example?
        
         | Supermancho wrote:
         | Traits are increasingly common.
         | 
         | https://en.wikipedia.org/wiki/Trait_(computer_programming)
        
       | AndrewDucker wrote:
       | It 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 wrote:
       | I 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.
        
       | vegetablepotpie wrote:
       | There'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 wrote:
         | > 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
         | 
         | 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
        
       | jonathanstrange wrote:
       | I 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 wrote:
         | > 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.
        
       | poisonborz wrote:
       | My 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.
        
       | EPWN3D wrote:
       | It 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 wrote:
         | Code example?
        
         | layer8 wrote:
         | "Protocols" is Apple-specific (or Objective-C/Swift-specific)
         | terminology. They correspond to types 1 and 2 of inheritance
         | that TFA mentions.
        
           | david2ndaccount wrote:
           | Python also has protocols: https://typing.readthedocs.io/en/l
           | atest/spec/protocol.html#p...
        
           | eyelidlessness wrote:
           | Protocols 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 wrote:
             | The 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.
        
         | shagmin wrote:
         | I'm guessing the closest equivalent in languages like Java or
         | C# are interfaces, right? Underutilized IMO.
        
           | neonsunset wrote:
           | Interfaces in C# are used way more often than abstract
           | classes and inheritance. It is also becoming more important
           | as there is more code written in Rust style with structs
           | implementing interfaces for zero-cost dispatch through
           | generic constraints e.g.                   void Handle<T>(T
           | value) where T : IFeature1, IFeature2...
        
       | seanwilson wrote:
       | Nobody 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 wrote:
         | Obligatory: Tailwind
        
           | seanwilson wrote:
           | I 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 wrote:
             | Tailwind is not a panacea--it has its own problems which
             | withhold adoption, "kneejerk reactions" notwithstanding.
        
             | danielvaughn wrote:
             | I'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.
        
               | explaininjs wrote:
               | Mainly because nobody is a "CSS Dev". If someone's entire
               | job was developing CSS, you might expect that they would
               | be willing to learn one new slightly different way of
               | doing it. But in reality the pushback comes from full
               | stack developers who already have a million other things
               | to worry about, such that relearning all the basic CSS
               | they already know, to achieve benefits that are rather
               | minuscule in the grand scheme of things, is a low
               | priority. I personally am working on a Tailwind product
               | that is fairly small (~10k lines, perhaps), and the
               | Tailwind isn't an annoyance per-say, but it's definitely
               | less ergonomically friendly than the well-engineered
               | enterprise application I was working on before, which was
               | 100x the size and used exclusively pure CSS.
               | 
               | Personally, my dream would be if inline styles supported
               | the full CSS gamut of pseudo selectors and the child
               | element selector. Then you'd have the admitted benefits
               | of not needing to synchronize the two files, along with
               | the benefit of not needing to relearn all of CSS.
               | 
               | Edit: it's funny, in a way, that all these developers
               | complaining about how CSS "doesn't scale" are likely
               | writing their Tailwind in an large scale application
               | styled entirely via CSS. https://github.com/search?q=repo
               | %3Amicrosoft%2Fvscode++langu...
        
               | danielvaughn wrote:
               | On the contrary, Tailwind's ergonomics are what attract
               | me to it. With the autocomplete features via the VSCode
               | intellisense plugin, I'm able to create UIs at a pretty
               | extraordinary pace.
               | 
               | As a trivial example, let's imagine we need to apply a
               | border radius to an element. Without Tailwind, it looks
               | like this:                 1. I find the element I need
               | to style       2. I look at which class it's using
               | 3. I navigate to my CSS file       4. I scroll down until
               | I find the selector       5. I type "border-r", then tab
               | on the auto-complete to fill in "border-radius"       6.
               | I type the colon character, then space       7. I think
               | about what unit is appropriate - rems, ems, pixels,
               | percentage       8. I think about what value is needed
               | for the design       9. I also look around to see if this
               | style is used elsewhere       10. If the style is used
               | elsewhere, I think about whether I need to refactor
               | 11. I type in the desired value       12. I type a
               | semicolon to mark the end of the statement       13. I
               | type cmd+s to save the css file
               | 
               | Here's the same example, with Tailwind:
               | 1. I find the element I need to style       2. I type
               | `round` and wait for the autocomplete to present my
               | options       3. I use my arrow key to select which one I
               | want       4. I type enter to add the desired tailwind
               | class       5. I type cmd+s to save the html file
               | 
               | The Tailwind interaction path takes less than half of the
               | concrete steps to complete. But it's even more dramatic
               | than that, because several of the steps taken in the
               | first example require enough thought that it breaks your
               | workflow and takes you out of your flow state. Then you
               | have to get back into that flow state to keep working.
               | But this keeps happening, so you're constantly stopping
               | and restarting. With Tailwind, I tend to find myself
               | staying in that flow state, because as I demonstrated
               | above, there's very little getting in my way.
        
               | seanwilson wrote:
               | Fully agree with this. The regular arguments against
               | Tailwind like "it's just inline styles", "learn CSS
               | properly", "it looks ugly" and "normal CSS is easy" say
               | nothing about how fast the Tailwind approach lets you
               | make edits and stay focused in comparison.
               | 
               | Normal CSS is usually worse than this too e.g. you hit
               | save, and your edit doesn't change anything, so you have
               | to use the web inspector to hunt down which class is
               | overriding your style then weigh up options for how
               | you're going to refactor while jumping between multiple
               | files. It's exhausting when you're trying to focus on
               | styling.
        
               | danielvaughn wrote:
               | I also assumed that a class already existed for it.
               | Because otherwise you have to think about whether you use
               | a class or an id or an element selector, you have to
               | think about what the class name is going to be, which
               | file it should go into, etc etc. What I presented was
               | _absolute best case scenario_ lol.
        
               | explaininjs wrote:
               | Only if you're refusing to consider inline styles. Which
               | is an odd decision to make if we're comparing to
               | Tailwind.
               | 
               |  _That said_ , I agree that this doesn't work for pseudo
               | selectors, very unfortunately, and I wish it would.
        
               | explaininjs wrote:
               | I actually just downloaded the VS Code extension earlier
               | today as a result of this discussion, perhaps that will
               | change my opinion. Because for me the two flows would be:
               | 1. I find the element I need to style        2. I
               | add/edit the inline style={{}} attribute I have for it by
               | typing `sty<TAB>{borrad<TAB>`        3. I add a value
               | 
               | VS:                  1. I find the element I need to
               | style        2. I add/edit the className attribute
               | 3. I pull up the tailwind documentation to find how to
               | type the CSS I already have memorized in their DSL (I
               | know some Tailwind by heart, but wayyyyy more CSS)
               | 4. I wait for it to load        5. I scroll down to find
               | the version of the class name that I need        6. I go
               | back to the editor and add the class.
               | 
               | Also a very common flow for me is to edit the CSS
               | directly in the browser, it's a much faster devloop than
               | the fastest live reload server. In that case it's far
               | easier to just copy from the `changes` view into the CSS
               | than go through and remap everything from CSS into
               | Tailwind.
        
               | danielvaughn wrote:
               | That's fair - styling directly in the browser does indeed
               | cut down several of the steps I mentioned, or at least it
               | condenses it to one step at the end. I do think that
               | installing the extension changes the experience entirely,
               | because then you don't need to reference the docs.
        
           | gentleman11 wrote:
           | Explain?
        
         | danShumway wrote:
         | I'm inclined to semi-agree with this, although I'm not sure I'd
         | be _quite_ as adamant about it myself. But I do generally think
         | that CSS is taught in a way that encourages some bad practice
         | around cascade /inheritance. I will point out though that (at
         | least in my experience), BEM solved the majority of these
         | problems for me even without a preprocessor. The language is
         | definitely oriented towards inheritance/cascade, but I think
         | there are ways to avoid it.
         | 
         | There's some movement towards ::part as a proposal to grant
         | some mixin behaviors (https://developer.mozilla.org/en-
         | US/docs/Web/CSS/::part) but I've never messed with it, and it's
         | applicable only to shadow DOM. But mixins haven't been a huge
         | issue for me in even enterprise-scale styling that I've done.
         | 
         | Opinion me, everyone has their own opinions on this, use
         | whatever CSS style works for you. This is not me saying that
         | BEM is the best for everyone, just giving a perspective that as
         | someone who tends to stick to vanilla CSS and who generally
         | kind of hates working with technologies like Tailwind or CSS-
         | in-JS, BEM-style vanilla CSS made CSS pretty pleasant for me to
         | work with; I have a lot more appreciation for the language now
         | than I used to.
         | 
         | So if you're annoyed by CSS but also get annoyed by pre-
         | processors or think that Tailwind is just inline CSS under a
         | different name[0], you still don't _need_ to be bound to the
         | cascade -- potentially look into BEM. No technology or
         | compilation or dependencies, it 's literally just a naming
         | convention and style guide.
         | 
         | ----
         | 
         | [0]: yes, I have used it extensively, please don't comment that
         | if I used it more something would magically click, I already
         | understand the points in its favor that you're going to comment
         | and I've already heard the style/framework suggestions you're
         | going to offer. It's fine if you like Tailwind, it's great if
         | it helps you write CSS, you don't need to convince me.
        
           | seanwilson wrote:
           | > But I do generally think that CSS is taught in a way that
           | encourages some bad practice around cascade/inheritance. I
           | will point out though that (at least in my experience), BEM
           | solved the majority of these problems for me even without a
           | preprocessor.
           | 
           | I think cascading is just a bad default, and I think
           | methodologies like BEM agrees with this by teaching you ways
           | to write CSS in ways that stops cascading from getting in the
           | way.
           | 
           | Cascading styles are fine for styling how basic document
           | content is shown (e.g. h2, p, a, li etc. tags) but outside of
           | this, you generally don't want the styles of parent elements
           | leaking into the styles of child elements.
           | Cascading/inheritance styles is a useful tool to have, but
           | not as the default.
           | 
           | I'm not saying Tailwind is perfect, but it's closer to
           | "prefer composition over inheritance", where you can sprinkle
           | in some cascading/inheritance where it makes sense.
        
             | spankalee wrote:
             | But BEM doesn't interact with the cascade. If you have two
             | BEM selectors (or a BEM selector and non-BEM selector) that
             | match an element and set the same property, the cascade
             | algorithm still applies to determine what to set the
             | property to.
        
               | danShumway wrote:
               | Sure, but the idea with BEM is that you generally don't
               | have situations where the result of that algorithm is
               | confusing or unexpected. Or at least that's been my
               | experience, even on large codebases. I generally don't
               | run into situations where styles overload each other in
               | weird ways when I'm using BEM (others' experiences might
               | vary).
               | 
               | You could throw the same criticism at Tailwind --
               | Tailwind can still expose you to cascade issues, not all
               | Tailwind classes are single-level selectors under the
               | hood and not all Tailwind classes only target one
               | property. At the end of the day this is all compiling
               | down to raw CSS, so in neither situation have you
               | actually eliminated the cascade. But with both BEM and
               | Tailwind you are much less likely to see those
               | situations, and when they do arise they are less likely
               | to introduce long-term maintenance problems and are more
               | likely to be easy to address/encapsulate. If you run into
               | cascade bugs with Tailwind, it's probably something you
               | fix in like one file, instead of needing to search
               | through five.
               | 
               | BEM doesn't technically interact with _anything_ , it's
               | just a style of writing CSS. There's literally no
               | technology behind it, it is just a naming convention. But
               | in practice, using a naming convention mitigates or
               | eliminates a large number of cascade issues.
        
             | danShumway wrote:
             | > I think cascading is just a bad default
             | 
             | I'm again semi-inclined to agree, I just don't think I'd
             | say it as forcefully; more that cascading styles tends to
             | have a lot of downsides that people aren't familiar with
             | and aren't taught.
             | 
             | My point isn't to badmouth Tailwind here; but debates about
             | this sometimes boil down to "CSS purists" vs "Tailwind
             | advocates" and my point is more -- nah, you don't have to
             | like Tailwind to avoid the cascade. You can be a CSS purist
             | and still avoid basic element selectors, your choice does
             | not have to be either "do semantic styling targeting only
             | semantic elements" or "jump on Tailwind and stick a bunch
             | of styles inline."
             | 
             | I'm more sticking up for -- look, if you're someone who
             | uses Tailwind, great, I don't have to tell you anything.
             | You are already using a framework that (regardless of any
             | other flaws it may or may not have) discourages you from
             | using the cascade. But if you're someone who's in the
             | position where you dislike CSS-in-JS or don't like using
             | Tailwind, also great! I'm in that position too, I don't
             | like Tailwind. But I still avoid cascade and basic element
             | selectors and there are ways to basically eliminate most
             | cascading styles from your codebase and eliminate most
             | cascade-caused bugs even if you aren't going to use a pre-
             | processor at all, and it's good to at least consider
             | removing those cascading styles.
             | 
             | My only critique of Tailwind I would bring here is that
             | sometimes I get the feeling that Tailwind advocates think
             | that Tailwind invented this idea of component-based CSS,
             | and it really didn't. But that's neither here nor there,
             | and if someone is using Tailwind and it works for them,
             | great. Life is way too short for me to argue with someone
             | using a technology that they enjoy. Honestly, same with the
             | cascade -- I think it can lead to long-term maintenance
             | problems, but if you like it, fine.
             | 
             | However, if you're using CSS and _hate it_ , and you also
             | don't want to use Tailwind, then give BEM a try.
        
         | vram22 wrote:
         | >It looks like composition over inheritance has caught on as
         | the better default in other languages
         | 
         | "Prefer composition over inheritance" was mentioned in an early
         | page of the GoF book (the Design Patterns book) - in 1994.
         | 
         | https://en.m.wikipedia.org/wiki/Design_Patterns
        
         | no_wizard wrote:
         | >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
         | 
         | I find this falls into generally two camps, those who want to
         | fight the browser and those who don't.
         | 
         | Those that tend to hate the cascade tend to fight the browser a
         | lot, whether they realize it or not. Generally speaking, they
         | want things to work a certain way (IE everything in isolation)
         | and prefer to think of styles isolated bits.
         | 
         | The second camp tends to not fight the browser and embrace the
         | browsers methodology. They embrace the cascade because it's
         | easier than fighting it, but it requires a more sophisticated
         | approach and seeing styles in a wholistic manner, not isolated
         | purely into components (though may be organized in a way that
         | co-located them with relevant components)
         | 
         | Both work, ultimately, and modern tooling and approaches allow
         | both to exist, but I will say, the second group often has a
         | better grasp on keeping project maintainability over time in my
         | experience.
        
           | coldtea wrote:
           | > _Those that tend to hate the cascade tend to fight the
           | browser a lot, whether they realize it or not. Generally
           | speaking, they want things to work a certain way (IE
           | everything in isolation) and prefer to think of styles
           | isolated bits._
           | 
           | So, wouldn't that be the browser fighting them, then?
           | 
           | They want something specific, and the broswer forces a
           | paradigm upon them that they don't want.
           | 
           | They are the humans and the browser (well, the styling
           | language of the browser) is the tool that should accomondate
           | them, not the other way around.
        
             | no_wizard wrote:
             | This is a sane argument. I have no objection to it. Both
             | things are true, in a sense.
             | 
             | It really depends on how you view the browser, IE should
             | browsers be more adaptive to certain paradigms or should it
             | set a reasonable paradigm and enforce it? I don't know that
             | there is a 100% right answer in this case, though they have
             | moved to create better hooks for some forms of isolation
             | (e.g. layers, scoping) but they fundamentally haven't
             | walked away from the cascade aspect.
             | 
             | It's converging the two paradigms, for sure, but as I said,
             | I don't think either is wholly incorrect or correct.
             | 
             | Now if you wanted my opinion on the whole thing, I think
             | the cascade is a fundamental element to be leveraged not
             | avoided, but that's me.
        
             | tonyarkles wrote:
             | If someone picks up a hammer and struggles to pound screws
             | in with them, it's not the hammer that's defective.
             | 
             | I don't think CSS is the perfect tool for all browser-based
             | styling but it's the tool that's there and it'll probably
             | work a lot better if you use it the way it's intended to be
             | used. If you want a screwdriver instead of a hammer... you
             | have options (don't target a browser, propose an
             | alternative to CSS, use something that compiles down to
             | CSS).
        
               | coldtea wrote:
               | > _If someone picks up a hammer and struggles to pound
               | screws in with them, it's not the hammer that's
               | defective._
               | 
               | If someone wants to hammer nails and they're given a
               | blender, then the blender might not be defective, but it
               | surely is not the right tool for the job, and it's
               | imposed upon them.
               | 
               | Few people ever loved CSS. The majority always either
               | hated it or learned to tolerate it. Most who do CSS today
               | use a few different paradigms on top to make them
               | tolerable like BEM, or use different transpilers to get a
               | better language, or directly control styling from code,
               | with CSS-in-JS libraries or like React does it.
               | 
               | > _I don't think CSS is the perfect tool for all browser-
               | based styling but it's the tool that's there_
               | 
               | Sure, I never denied its existance. Just its design.
        
           | cogman10 wrote:
           | > the second group often has a better grasp on keeping
           | project maintainability over time in my experience.
           | 
           | That's probably in the eye of the beholder and a necessity.
           | If you are all in on the cascades then you have to limit the
           | depth of your page structure, otherwise it becomes impossible
           | to predict how things will ultimately render or what the
           | impact of a change at layer 3 will have on the rest of the
           | page.
           | 
           | Technically speaking, isolation is perfectly possible with
           | webcomponents.
        
             | no_wizard wrote:
             | Yes, there are more tools for isolation now, but you can't
             | wholly opt out of the cascade, even if it's only local
             | relevant, and I think that's a good thing IMO
        
           | quaunaut wrote:
           | I'd ask what you mean by "fighting the browser"- as
           | generally, the number one way to ruin the performance of your
           | CSS is to introduce depth to it. In general, keeping
           | everything isolated regularly leads to better rendering
           | performance.
        
             | no_wizard wrote:
             | Avoiding the cascade at all costs, for example. It can
             | introduce a lot of unintended consequences.
             | 
             | Another anti pattern I have seen is the over use of media
             | queries to force the browser to do certain things rather
             | than embracing relative sizing constraints via intrinsic
             | design and letting the flexbox and grid algorithms do most
             | of the heavy lifting.
             | 
             | Here though I want to point out isolation is relative, as
             | is the cascade. I think it's important to leverage the
             | cascade wherever you can but that doesn't mean you are
             | leveraging it from top to bottom per say, but it does mean
             | thinking more wholistic about the context of styling
        
         | layer8 wrote:
         | It's not only inheritance, it's also lack of encapsulation and
         | of separation of concerns, which leads to attack vectors like:
         | https://news.ycombinator.com/item?id=39928558
        
         | ramijames wrote:
         | I really agree with this.
         | 
         | There was a company that I worked at 10ish years ago with a
         | SaaS built around Drupal. They had one monolithic css file that
         | was 25000 lines. It was a nightmare. When I took over the
         | front-end rebuild, went through it line by tedious line and
         | broke it out into discrete components.
         | 
         | I think about that from time to time when building logic or
         | other functionality. I'd much rather have a self-contained bit
         | of code than a big thing that is so intertwined I'm terrified
         | to touch anything.
        
         | cjpearson wrote:
         | CSS does actually call it inheritance[0], but it's commonly
         | mixed up with the cascade. Inheritance applies to certain
         | properties, so that when they are not specified on an element,
         | an element inherits the value of the parent.
         | 
         | The cascade[1] determines how rules from multiple sources are
         | merged.
         | 
         | [0]: https://developer.mozilla.org/en-
         | US/docs/Web/CSS/Inheritance [1]:
         | https://developer.mozilla.org/en-US/docs/Web/CSS/Cascade
        
         | spankalee wrote:
         | The cascade is not much like inheritance at all. If anything
         | it's more like composition because the styles can come from
         | multiple sources, eg user styles vs author styles, multiple
         | layers.
         | 
         | The cascade is so much not like inheritance that I wonder if
         | you meant either the concept of selectors in general, or
         | inherited properties?
        
           | seanwilson wrote:
           | Yeah, the other poster pointed out
           | https://developer.mozilla.org/en-US/docs/Web/CSS/Inheritance
           | and https://developer.mozilla.org/en-US/docs/Web/CSS/Cascade.
           | 
           | I meant the concept that when you e.g. apply the style
           | "color: blue;" to an element, all child elements get the same
           | style unless you override it. I'm not saying it's identical
           | to inheritance, but it's similar in that changes and
           | additions at the top-level ripple down to lower levels, which
           | I find causes most of the problems when writing maintainable
           | code.
        
             | danShumway wrote:
             | > I meant the concept that when you e.g. apply the style
             | "color: blue;" to an element, all child elements get the
             | same style unless you override it.
             | 
             | This is the first thing you've said so far that I would
             | push back against. Neither BEM nor Tailwind removes this
             | behavior that I'm aware of. I thought when talking about
             | the cascade you meant generic styles on elements like "p",
             | "ul", etc... getting applied across separate components, or
             | specificity of child selectors, or something similar.
             | 
             | If you really dislike styles being applied to children in
             | the DOM that don't override those styles, I don't think
             | there is a way around that other than web-based components
             | and shadow DOM with isolated styles. Or I guess use a bunch
             | of style resets beforehand I guess? Neither Tailwind nor
             | BEM gets rid of child inheritance of applied styles; you
             | can use @layer I guess, but that doesn't get rid of that
             | behavior either, it just allows you a bit more control over
             | style order.
             | 
             | If you're using Tailwind and you write:
             | <div class="text-red-400">          <p>Some text</p>
             | </div>
             | 
             | that text will be red. If you're using BEM and you write:
             | <div class="Container">          <p>Some text</p>
             | </div>            .Container { color: red; }
             | 
             | same deal.
        
               | seanwilson wrote:
               | > I thought with inheritance you meant generic styles on
               | elements like "p", "ul", etc... getting applied across
               | separate components
               | 
               | Yes, so I mean if you add "color: blue" to "p", it's now
               | going to start interacting with any element that's a
               | child of "p" (which will probably be on all pages on your
               | website so hard to predict and check what will happen).
               | 
               | BEM and Tailwind don't get rid of the behaviour of the
               | color being applied to child elements, but it at least
               | forces you to isolates these kinds of style changes to
               | the component level (vs sitewide) which is what improves
               | maintainability.
        
               | danShumway wrote:
               | Okay, we are on the same page then -- sorry. Yep, I
               | generally agree with this.
        
             | cjpearson wrote:
             | I can see how it causes problems, but I don't really see
             | any alternatives for certain properties. Would you want to
             | set the color property on every element that contains text?
             | Or do something with the universal selector?
        
       | dboreham wrote:
       | It'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 wrote:
         | Can you explain how any construct in FP conflates subtyping and
         | code sharing the way OO inheritance does?
        
         | tossandthrow wrote:
         | you 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.
        
       | FrustratedMonky wrote:
       | Oh, 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 wrote:
         | It's funny how easy it is to spot a ChatGPT reply
        
           | FrustratedMonky wrote:
           | Do you think it is because it is too verbose? Long winded? A
           | human wouldn't bother to write that much?
        
       | constantcrying wrote:
       | I 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 wrote:
         | I 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 wrote:
           | I 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 wrote:
         | This 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 wrote:
       | I 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 wrote:
         | Ha 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
        
           | reactordev wrote:
           | Ahhh good 'ol XP/Gang of 4. Never ceases to be referenced.
           | (for good reason)
        
           | ben7799 wrote:
           | Pretty 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 wrote:
             | He he. Java and patterns, Java and frameworks (mumble
             | Spring mumble IoC mumble dependency injection), Java and
             | factories and builders, mumble <buzzword> ...
        
         | ruszki wrote:
         | > "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.
        
           | maleldil wrote:
           | > TypeScript made it quite obvious what's possible only with
           | simple type checking.
           | 
           | Typescript's type system is anything but simple.
        
       | d_burfoot wrote:
       | It 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 wrote:
         | Java 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 wrote:
           | So 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 wrote:
             | No one is more confident in their abilities than a boot
             | camp grad.
        
           | tootie wrote:
           | There 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.
        
           | josephg wrote:
           | > There are many types of large scale systems where I would
           | only code it using the JVM, everything else is a nightmare.
           | 
           | I've seen more than one lifetime's worth of nightmare code
           | written in Java. I agree with the GP commenter - I don't
           | think the problem is Java (the language) so much as the
           | culture surrounding it. The Java programmers I've worked with
           | (all smart people) seem addicted to solving all problems my
           | writing more Java. More interfaces (most of which just have a
           | single implementor). More classes. More files. More
           | abstractions. You pay a massive tax for that any time you
           | edit your software - since inevitably you're going to end up
           | unpicking hundreds of lines of code that could have been two
           | if statements.
           | 
           | I've never seen this abstraction vomit disease be quite this
           | bad in software written in other languages. You can find a
           | bit of it in C#, C++, go and Python. But not as bad as Java.
           | In typescript and rust, most of the code I work with focuses
           | a lot more on directly solving the actual problem at hand.
           | 
           | I don't think the problem is the language itself. Java is a
           | fine language. But for some reason, it's become a magnet for
           | a certain kind of developer that I frankly never want to work
           | with. Developers who would never use 5 lines of code when 100
           | would do. Developers who abstract first and understand the
           | problem they're solving later. It's disgusting.
        
         | tootie wrote:
         | I have often observed that Java is great because it solves
         | organizational problems. Strong and static types, interfaces
         | and most of all, javadoc, were miraculous for conveying intent
         | to integrators and maintainers. Even seemingly insignificant
         | things like a naming convention for packages was extremely
         | useful. A lot of the features trumpeted in Java when it was
         | new, like abstract classes or checked exceptions, were adopted
         | religiously at first but fell out of favor a long time ago.
         | Interfaces and interface inheritance is extremely useful.
        
         | HumblyTossed wrote:
         | Yeah, there's nothing wrong with Java the language. Java the
         | community is what sucks. You had a few bloggers get popular and
         | then a bunch of devs trying to make names for themselves would
         | ape what they said, compound a few years and you have the mess
         | people like to complain about.
         | 
         | Happens with a lot of language communities. C#, good grief, not
         | much better than Java. Go(lang) community's favorite two words
         | are "idiomatic Go". You can't read any post without seeing
         | those two words. Go has like what, seven keywords. Let it go,
         | man.
        
         | gorjusborg wrote:
         | > there's a huge industry of people critiquing Java and OOP but
         | misdiagnosing the problems as technical rather than
         | sociological.
         | 
         | I totally agree. I have worked on applications that would have
         | collapsed under their own weight if it wasn't for Java. The
         | strong type system, stable runtime and standard library APIs,
         | as well as great tooling allows code to live far longer than it
         | would in some other language ecosystems.
         | 
         | However, don't make the mistake of dismissing all of the
         | criticism. The truth is often nuanced, and OOP/Java does have
         | some serious downsides and footguns. There is room for both
         | criticism and praise.
         | 
         | Ultimately, great systems aren't created by throwing a language
         | and problems at a collection of random people. Great systems
         | happen when good decisions are consistently made over time, and
         | those are context dependent.
        
       | not2b wrote:
       | I 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 wrote:
         | But 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.
        
       | arcticbull wrote:
       | Boring 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 wrote:
         | Main 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 wrote:
           | What 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 wrote:
             | Any kind of usable GUI library. Plus I am doing 3d
             | graphics, and rust is not there yet.
        
             | bluGill wrote:
             | All the company specific libraries we have debugged of the
             | past 30 years.
        
       | layer8 wrote:
       | It'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 wrote:
       | When 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 wrote:
       | My 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 wrote:
         | I 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.
        
       | turnsout wrote:
       | I 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 wrote:
         | Inheritance on interfaces is fantastic. Inheritance on
         | implementations is fraught with peril. Java developers figured
         | this out like 10 years ago.
        
         | josephg wrote:
         | I 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 wrote:
           | Rust 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 wrote:
         | Despite 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 wrote:
       | For 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 wrote:
         | I 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 wrote:
       | The 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 wrote:
         | 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 wrote:
           | > 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 wrote:
             | When 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/Abs
             | tract...
        
               | quaunaut wrote:
               | I 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.
        
         | taylodl wrote:
         | Prefer IS-A relationships where they make sense, i.e. where you
         | expect the Liskov Substitution Principle to apply. Otherwise
         | use composition. It's really that simple.
        
         | quaunaut wrote:
         | I'd be ready to agree if I could be pointed at a time that
         | inheritance actually carries a real benefit- a time you _would_
         | choose it over composition, if composition is available.
        
         | bsder wrote:
         | Inheritance was a _premature optimization_ for computers with
         | small memory. It did its job. However, once memory got big,
         | people forgot to throw it out.
         | 
         | Composition uses a lot more indirection. That's _bad_ on modern
         | CPUs. Pointer chasing throws out performance, so composition is
         | _not_ always preferred, either.
        
       | fire_lake wrote:
       | Hmmm. I have exactly zero occurrences of implementation
       | inheritance in my code base.
        
         | feoren wrote:
         | Exactly. "Why does everyone use it?" is a false premise. I
         | don't use it.
        
       | quantified wrote:
       | Meyer also thought (for a while) that inheritance was more
       | powerful than genericity. Later he admitted his "proof" of that
       | was weak.
        
       | lamontcg wrote:
       | Inheritance 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).
        
       | m463 wrote:
       | I found multiple inheritance kind of fun when I was first
       | exploring python.
       | 
       | Then, madness...
        
       ___________________________________________________________________
       (page generated 2024-04-11 23:01 UTC)