[HN Gopher] Goodbye, shitty Car extends Vehicle object-orientati...
       ___________________________________________________________________
        
       Goodbye, shitty Car extends Vehicle object-orientation tutorial
       (2011)
        
       Author : Tomte
       Score  : 117 points
       Date   : 2022-06-24 08:20 UTC (14 hours ago)
        
 (HTM) web link (www.mail-archive.com)
 (TXT) w3m dump (www.mail-archive.com)
        
       | substation13 wrote:
       | The OOP community fascinates me.
       | 
       | It's clear that OOP in the sense of "Car extends Vehicle" is not
       | a good idea, yet the classic OOP languages go out of their way to
       | support this kind of programming. Meanwhile the alternative
       | approaches require you to jump through a bunch of hoops or apply
       | convoluted "design patterns". Writing good, modern "OOP" you are
       | often fighting the OOP language! Things are getting somewhat
       | better (e.g. Java Record types) but perhaps it is time to admit
       | these languages just aren't a good fit?
        
         | fabian2k wrote:
         | The only real difference is that inheritance as explicit
         | language support while composition is something you can easily
         | implement with common language features. It's not really more
         | difficult to use composition than inheritance.
         | 
         | As an example, if I want to use composition in C# using
         | ASP.NET, I simply inject the class I want to use into my new
         | class using the built-in DI. This is as simple as extending the
         | class.
        
           | substation13 wrote:
           | Some features are plain missing (or must be implemented in a
           | convoluted manner):
           | 
           | - Discriminated unions
           | 
           | - Immutable data types (records)
           | 
           | - Free functions (or modules of free functions)
           | 
           | - Software transactional memory (STM), atoms
           | 
           | - Ad-hoc polymorphism (traits)
           | 
           | - Custom operators (used sparingly for building DSLs)
           | 
           | - List comprehensions
           | 
           | Instead we get anti-features like automatic properties,
           | implementation inheritance, events, object initializers,
           | etc...
        
             | tester756 wrote:
             | >(records)
             | 
             | there are
             | 
             | >- List comprehensions
             | 
             | Enumerable.Range?
             | 
             | >- Custom operators (used sparingly for building DSLs)
             | 
             | Could you please show what kind of DSL you'd want to write?
        
         | tgv wrote:
         | > It's clear that OOP in the sense of "Car extends Vehicle" is
         | not a good idea
         | 
         | It's not a bad base for UI programming. There is quite a lot of
         | functionality that is suited to the OOP paradigm.
        
         | everyone wrote:
         | It's really not. For example C# has no guiding principles imo.
         | It's just a hodgepodge of any and all popular language features
         | all lumped together. Java was popular at its inception so it
         | stole lots of Java, as new things have become popular they are
         | added (eg. anonymous functions)
         | 
         | As a dev you just use whatever features u like and can safely
         | ignore the majority of them. Its very non-prescriptive unlike
         | some languages where you must do a thing only one way.
         | 
         | Eg. Inheritance is pretty much terrible, I just never use it.
         | Same way I try to not write bad code and instead write good
         | code.
         | 
         | Also sure you need to put functions in classes, but thats fine,
         | classes are just buckets for code. In any language you need to
         | put your functions _somewhere_ eg. in a text file.
        
         | lmm wrote:
         | It suits cases (e.g. modelling business logic) where you have
         | that kind of edge case. Until recently non-OO languages had
         | very little support for delegation, and so while inheritance
         | bundles delegation with some other things it really shouldn't
         | be bundled with, it was often the least-bad option. In
         | particular this made it a natural fit for GUI, where you very
         | often want "this widget is almost like this one, except...",
         | and so it was able to ride the coattails of that.
         | 
         | Add on the fact that people somehow seem to think Alan Kay has
         | some insights (even if they can never tell you what they are),
         | and Bob's your uncle.
        
           | substation13 wrote:
           | > non-OO languages had very little support for delegation,
           | 
           | What do you mean by "delegation" here?
        
             | lmm wrote:
             | I mean something like "this value contains this kind of
             | value and implements the same interface as it by forwarding
             | methods to the corresponding methods on that". E.g.
             | https://kotlinlang.org/docs/delegation.html . (i.e. imagine
             | doing something similar to inheritance but having the
             | "parent" object be explicitly a field of your child object
             | rather than just mushed together with it).
        
               | substation13 wrote:
               | Ah I see. What you say is not quite true though, ad-hoc
               | polymorphism (traits) provides the same functionality for
               | non-OOP languages.
               | 
               | > Until recently non-OO languages had very little support
               | for delegation
        
       | mikojan wrote:
       | It's not OO tutorials, it's OO.
        
       | nuc1e0n wrote:
       | But OOP was specifically created to model real world objects and
       | their relationships. If this article is saying OOP doesn't do
       | that very well and we should not think in those terms, what's it
       | good for?
        
       | DeathArrow wrote:
       | After many years of OOP, I'm not sure if we gained anything from
       | OOP, and if it wasn't a solution in search for a problem.
       | 
       | At first I was enthusiastic. Then I've realized that there might
       | be better ways to solve problems than trying to fit everything in
       | a "everything is an object" mentality.
       | 
       | OOP can lead to overly complex code, uneeeded abstractions and
       | design patterns thrown on top of each other for no good reason.
       | 
       | There was a thinking that using objects you can model or imitate
       | real life, but that is silly. And of course, programming doesn't
       | deal with real life, but with bytes and instructions, data and
       | actions performed on the data.
       | 
       | I still use OOP because most of the industry demands it, but when
       | I can, I try to use a data oriented and a bit of a functional
       | approach, even if I am using an OOP language.
        
         | system16 wrote:
         | > "The problem with object-oriented languages is they've got
         | all this implicit environment that they carry around with them.
         | You wanted a banana but what you got was a gorilla holding the
         | banana and the entire jungle." - Joe Armstrong
        
         | mynameisash wrote:
         | I'm reminded of the "Object oriented programming is bad"
         | series: https://youtu.be/QM1iUe6IofM
        
           | cloogshicer wrote:
           | It's really great, highly recommend it!
        
           | codemonkey-zeta wrote:
           | For anyone unfamiliar, I recommend the whole series, but it
           | also culminates in the presenter rewriting an entire
           | nontrivial OOP program into a procedural one and saving
           | whopping amounts of code/complexity. The big example helps to
           | show this isn't just abstract rambling against OOP.
           | 
           | https://m.youtube.com/watch?v=V6VP-2aIcSc
        
             | magicalhippo wrote:
             | To me, his title is somewhat misleading though. It seems
             | he's against the extreme "everything is an object" version
             | of OOP. I think taking things to the extreme is almost
             | always a bad thing. Sometimes goto _is_ a good solution.
             | 
             | I do agree with most of his points though, which just so
             | happens to be closely aligned with how I've ended up
             | programming. I like to use interfaces, so my objects
             | hierarchies are very shallow and use a lot of delegation,
             | overriding few if any methods.
             | 
             | I also write a lot of free-standing functions, some long,
             | when I feel that's best. I also mix in a fair bit of
             | functional-like programming, especially when massaging
             | data.
        
         | qsort wrote:
         | For all its blemishes it's still the most natural way to
         | represent the concept of "user-defined data type" in a
         | procedural language.
         | 
         | "Modeling the world as OOP", OTOH, is a load of bull.
        
           | igouy wrote:
           | "It should be clear to anyone that models of the world are
           | completely different from models of software. The world does
           | not consist of objects sending each other messages, and we
           | would have to be seriously mesmerised by object jargon to
           | believe that it does.
           | 
           | ... we use different sets of building blocks for modelling
           | the world and modelling the software ..."
           | 
           | 1994 "Designing Object Systems"
           | 
           | https://www.google.com/books/edition/Designing_Object_System.
           | ..
        
             | Konohamaru wrote:
             | What software would you use if you wanted to model the
             | world?
        
               | igouy wrote:
               | Sorry, I'm not up-to-date with modelling software.
        
         | hota_mazi wrote:
         | > OOP can lead to overly complex code, uneeeded abstractions
         | and design patterns thrown on top of each other for no good
         | reason.
         | 
         | All programming paradigms can do that, you are not making any
         | particularly salient point about OOP here.
         | 
         | OOP doesn't mean that everything needs to be an object, but
         | inheritance and shared behaviors are concepts that map neatly
         | between the real world and the programming world, which is why
         | OOP has been such a resounding success.
         | 
         | We have refined this approach now (e.g. using delegation over
         | inheritance), but these basic concepts of shared behavior and
         | reuse remain.
        
         | randyrand wrote:
         | I just wrote 60k lines of C code and have never once felt the
         | need for OOP. And I say that as someone who has spent their
         | whole career with OOP.
         | 
         | If I need some functionality, I write a function and pass it a
         | struct. I've yet to find a time when this approach is too
         | limiting, or chaotic.
         | 
         | Even for interfaces, function pointers achieve that goal
         | entirely.
         | 
         | I do miss type safe vectors, e.g templating. Thats above and
         | beyond the most common thing I miss.
        
           | bighuh wrote:
           | Any OOP course covers the 'reasons' for OOP (such as they
           | are). Polymorphism, inheritance, and encapsulation. Yes, you
           | can model much the same behavior with structs and associated
           | functions (which is really still OOP to some extent, just
           | without any language support), but it's quite a bit of
           | additional work.
        
             | Symmetry wrote:
             | Late binding polymorphism and encapsulation are both very
             | important concepts but there's no particular reason that
             | your unit of inheritance should be your unit of
             | polymorphism, often the boundaries should be drawn
             | differently. And inheritance is just a way of achieving
             | code re-use that tends to make suboptimal tradeoffs between
             | flexibility and DRY.
             | 
             | There's a certain conceptual and implementation elegance to
             | OoO so I understand why it became popular. But in practice
             | all the new languages moving away from it are doing so for
             | good reason.
        
           | pphysch wrote:
           | Bound methods simply has the potential to be a tiny bit more
           | readable:
           | 
           | `thing.doWork(arg)`
           | 
           | `doWork(thing, arg)`
           | 
           | `doThingWork(thing, arg)`
           | 
           | The first example is _marginally_ more readable IMO. The
           | second one sucks from a human perspective because we don 't
           | immediately know (unlike the IDE) that the function signature
           | only accepts Things. The last one addresses this but is even
           | longer, and we still have to mentally unpack the args.
           | 
           | IMO there is not much profound about OOP, but it can lead to
           | slightly more readable code if used appropriately.
        
             | random314 wrote:
             | I kinda agree. But my personal take is different.
             | 
             | Functions can be invoked in at least 3 different ways or a
             | mixture of the three
             | 
             | 1. Prefix - C style f(x,y,z)
             | 
             | 2. Infix - x + y; x.f(y,z); a join b
             | 
             | 3. Post fix- (x,y,z) $ f
             | 
             | Infix is the best style in most cases, OOP accidentally
             | helped programmers use a more readable infix style.
             | 
             | A lot of the benefits of OOP can be had by allowing good
             | infix invocation support and interface support in an
             | imperative non OOP language. Might as well throw in
             | generics.
        
               | pphysch wrote:
               | Yes, you put it better.
               | 
               | Infix is tricky because it doesn't scale well to complex
               | expressions. I think Python does it right with plenty of
               | infix keywords a la "if thing not in myset: ...".
        
             | blinzy wrote:
             | Is your first/native language SVO (subject-verb-object)?
             | Mine isn't, it's VSO (verb-subject-object) or SOV (subject-
             | object-verb), and for me the first example isn't more
             | readable so I wonder if that also changes our perspective
             | on what option we see as the most natural.
             | 
             | I can admit though that from an IDE point of view having
             | auto-completion upon writing thing and typing the dot and
             | seeing the results is very useful, whereas there is nothing
             | (as far as I know) that comes even close in functional
             | languages where the second example is more typical.
        
         | 202206241203 wrote:
         | OOP still rules the GUI. Even if you spread some functional
         | sauce all over it.
        
           | corrral wrote:
           | As one can see in React, which contorted itself to be
           | "functional" and ended up inventing bad, new syntax for
           | objects in a language that already had objects built-in. They
           | evidently couldn't figure out a better way to handle it, and
           | landed on reinventing the wheel, badly, and but calling it
           | something else.
        
             | hellohowareu wrote:
             | Eh, considering its popularity I suppose it is a "least
             | bad" if one chooses a cynical yet realistic perspective.
             | 
             | Personally, after working with vanilla JS, then jquery, I
             | looked at angular and react. I liked react so much better
        
         | vsareto wrote:
         | Good OOP stuff is usually in a standard library or has to do
         | with computer science stuff instead of business applications.
         | Writing OOP style services and repositories and real-world
         | object hierarchies (Prius inherits from Car and implements
         | IVehicle) is where it sucks.
        
         | igouy wrote:
         | > ... using objects you can model or imitate real life, but
         | that is silly.
         | 
         | 1994 "It should be clear to anyone that models of the world are
         | completely different from models of software. The world does
         | not consist of objects sending each other messages, and we
         | would have to be seriously mesmerised by object jargon to
         | believe that it does.
         | 
         | ... we use different sets of building blocks for modelling the
         | world and modelling the software ..."
         | 
         | page 6 "Designing Object Systems"
         | 
         | https://www.google.com/books/edition/Designing_Object_System...
        
         | dgb23 wrote:
         | The best thing that object orientation popularized is the
         | interface/protocol abstraction. All the modern languages I
         | learned put this front and center: Go's interfaces, Clojure's
         | protocols and multimethods, Rust's traits.
         | 
         | This apparently goes back to part of the inspiration of
         | Smalltalk (dataless programming). It enables you to create an
         | ubiquitous language and radically improves your ability to
         | structure a program in terms of reusable abstractions. It is
         | fundamentally about hiding complexity and specificity on one
         | end and only requiring or assuming what you _need_ to require
         | or assume on the other.
         | 
         | In comparison to this, inheritance feels like a half baked
         | implementation detail that got out of hand. Especially static
         | inheritance hierarchies feel like the antithesis of object
         | orientation. Pinning everything down and turning a codebase
         | into immovable concrete.
        
           | Banana699 wrote:
           | >OOP to me means only messaging, local retention and
           | protection and hiding of state-process, and extreme late-
           | binding of all things.
           | 
           | - Alan Kay -
           | 
           | Other remarks from him about OOP are (paraphrased):
           | 
           | - OOP is a recursion on the notion of the computer itself
           | 
           | - The internet\Erlang is more OOP than java
           | 
           | His vision for OOP is something akin to distributed
           | computing: Each object is a seperate computer with its own
           | private memory that absolutely nobody can write to, it
           | presents to the world an interface which is the set of all
           | messages it can respond to (messages might or might not be
           | asynchronous, and might or might not be unreliable in
           | order,delivery,etc...). "Late Binding" because you have zero
           | gaurantees about anything not explicitly stated in the
           | object's contract, anything the object doesn't promise to
           | respect will be violated, you better not depend on it. This
           | is to give freedom to as much different implementation of the
           | same interface as possible.
           | 
           | Two other analogies I encountered are
           | 
           | - Objects are like hardware ICs (their interfaces are the
           | external pins and the type of signals the IC expects on them,
           | their internals is the private hardware and signals that
           | nobody is expected to inspect or modify)
           | 
           | - Objects are like seperate programs (their interfaces are
           | the GUI/cmd the program presents and the set of all possible
           | actions you can do, their internals are the executable code
           | and in-memory data structures that nobody is expected to
           | inspect or modify)
           | 
           | Inheritance actually precedes mature OOP, it was introduced
           | way back in Simula-62, the proto-OOP language that wasn't a
           | proper language as much as a simulation framework.
           | Inheritance makes perfect sense in a simulation framework,
           | because you're modelling a small world with a definite
           | ontology that you can articulate and be sure of, the creators
           | of Simula stumbled on it when they found they were repeatedly
           | duplicating code verbatium. The analogies of Car<Vehicle and
           | Cat<Animal make perfect sense, this is what inheritance is
           | really made for, it's just that most real software isn't
           | discrete event simulations with platonic ontologies.
        
             | jerf wrote:
             | I respect Alan Kay as a programmer and an intellectual, but
             | the plain and simple reality is that his definition of OO
             | is not the one that carried the day, and his definition has
             | very little relevance in the world today.
             | 
             | If one squints hard enough, one could argue that we are
             | continuing to move in his direction even so, slowly and in
             | fits. But it's not because we have an Alay-Kay-OO language,
             | and to the extent it is happening, it's not clear that it's
             | happening at a level of abstraction that languages matter
             | much with. When people use OO as a term today, it's a
             | different definition.
        
         | tester756 wrote:
         | >I'm not sure if we gained anything from OOP,
         | 
         | Yea, sure, we didn't gain anything.
         | 
         | Except milions of programmers being able to model complex real
         | world into their software that powers almost everything now.
         | 
         | Even if it is "shitty" "hard to maintain"
         | 
         | then it at least works
        
         | jacobsenscott wrote:
         | OOP was a big step up from the procedural programming that
         | dominated at the time. Unfortunately that step was kicked out
         | from underneath us because the industry chose C with Class and
         | Java, rather than Smalltalk, or CLOS, or even objective C.
        
       | bdg wrote:
       | I actually like to ask people in interviews to model a car and
       | car factory because I want to see right away if they go deep into
       | the nonsense of extending everything, or if they can use
       | composition, or get away with something focused on maintainable
       | code that meets requirements.
       | 
       | I've seen it all. Prius extends Car extends vehicle extends motor
       | extends ...
       | 
       | Right within the first five minutes of the interview.
       | 
       | But I also think these old "a bike extends a " tutorials were the
       | most frustrating thing in the world and made no sense to me when
       | I was trying to learn OOP in the first place. From my memory, I
       | recall all the examples I could find were absolutely nonsense but
       | blogs were full of posts with lots of non-real-world about
       | connecting web requests things to a DB code and lots of "yeah
       | it's just like a person is-a mammal which is-an animal which...".
       | 
       | It wasn't until I wrote some c++ and looked at how it worked in
       | the heap and stack before I started to understand it better. Then
       | later I learnt about SOLID and discovered that you almost never
       | really want to use extension in practice (at least in the
       | problems I worked on), except for things like interfaces in
       | places you expect (and plan to have, and even better if you
       | already do have) a reason for variations in sub-types that have
       | the same contract, or need to cross system boundaries.
       | 
       | I almost always want to use composition. I will use extensions
       | but this should be really specifically be selected for a few key
       | points.
        
         | 734129837261 wrote:
         | In job interviews, people don't write the code they would write
         | normally, they write the code that they think the interviewer
         | wants to see.
         | 
         | And that brings me to a topic that's entirely different but
         | also very relevant: job interviews bring interviewer-biases
         | with them.
         | 
         | If you run into an old-school interviewer who would do exactly
         | that "Prius extends Car extends Vehicle, etc." nonsense, but
         | you don't know it, they would rate you negatively.
         | 
         | If you run into someone who is just in love with functional
         | programming, you'll lose any OO implementation.
         | 
         | If you run into someone who doesn't like it when you ask
         | questions, you'll lose. If you run into someone who doesn't
         | like you asking questions they don't know the answer to, you
         | lose.
         | 
         | And if you get sent a 3-hour long Hackerrank or Leetcode
         | algorithmic test, everybody loses.
         | 
         | Tech job interviews are just horribly biased and the game is
         | won if you read your audience correctly. And even then, if the
         | other person is a racist, or just doesn't like your face, or
         | had a bad day, or feels threatened, or disapproves of how you
         | write "Javascript" instead of "JavaScript", you still lose.
        
           | onpensionsterm wrote:
           | Not including the racist discrimination example, I'm not sure
           | that this is a bad problem. I believe there is a tendency for
           | us to leave the schooling system and start viewing interviews
           | as though they were our new exams, where failure is your own
           | fault. I argue that interviews are the starting point of a
           | new relationship, so the closest analogy is actually a date.
           | If you're on a date and express your values and the other
           | person rejects you for it, it simply means you don't make a
           | good relationship - even if it's just because the other
           | person has unfair demands.
           | 
           | So don't project programming ideals you don't believe in. If
           | they can't reconcile different opinions, it's their problem.
           | You won't have the conviction or experience to do it
           | convincingly anyway.
        
             | kevinmchugh wrote:
             | And if they don't like you asking questions, you've really
             | dodged a bullet!
        
           | zdragnar wrote:
           | > If you run into someone who doesn't like it when you ask
           | questions, you'll lose. If you run into someone who doesn't
           | like you asking questions they don't know the answer to, you
           | lose.
           | 
           | That's actually a win in your book, not a loss!
        
           | im3w1l wrote:
           | Just thinking here, but what if you take the guesswork out of
           | it "Here at Company X our philosophy is to avoid deep class
           | hierarchies. How would you model..."
        
           | linuxftw wrote:
           | It's always this. Every interview I've had is a quest for the
           | interviewer to demonstrate they know more about the subject
           | they quiz me about than I do. This is especially true with
           | kubernetes, it's a vast project with many areas, but if you
           | don't know whatever niche the interviewer is interested in,
           | you will struggle.
        
           | BoorishBears wrote:
           | Every example you listed would be dodging an enormous bullet.
           | 
           | The exact example the grandparent used was essentially a
           | wrong answer, so our "old-school interviewer" (that sounds
           | kind of ageist doesn't it?) marking us down for not using it
           | is a bad thing.
           | 
           | An interviewer who loves functional programming and doesn't
           | communicate any preference then marks you down for not
           | reading their mind is someone to avoid.
           | 
           | An interviewer who punishes you for asking questions is a
           | _huge_ red flag and you 'll be dodging a _huge_ bullet.
           | 
           | I'm a self-taught dev so I definitely have some thoughts on
           | how tech interviewing goes, but an interview runs both ways.
           | I'd much rather miss out on a job because the other person
           | was racist or they hate when people ask questions, than to
           | end up working with them.
        
             | Avshalom wrote:
             | "Sure I missed rent and now I have an eviction on my record
             | and I'm sleeping in a car but at least I didn't get a job,
             | bullet dodged"
        
               | BoorishBears wrote:
               | "Your advice doesn't work out in every single situation
               | ever experienced by a human so I'm going to come up with
               | a sarcastic comment that says nothing of value to point
               | that out."
               | 
               | You'd think common sense would tell one not to define
               | what's good for them in general based on what's good for
               | you when they're "missing rent with an eviction and
               | sleeping in your car".
        
             | yCombLinks wrote:
             | Not every person in the interview process is anywhere near
             | you in the org though, sometimes great jobs are behind a
             | shitty interview
        
               | bdg wrote:
               | Yes, and sometimes shitty jobs are behind great people.
               | Candidates get catfished in interviews all the time.
        
               | BoorishBears wrote:
               | In my experience there are few great jobs behind shitty
               | people. Great jobs tend to not put shitty people in the
               | hiring loop.
               | 
               | Similar works backwards, if you're using the interview to
               | also ask the right questions, good people are generally
               | not going to lie to you...
        
           | bdg wrote:
           | Yes, our industry has a lot of inexperienced managers and
           | interviewers who don't know what to evaluate or how to
           | clarify the goal for a candidate.
           | 
           | I think this is a different challenge related to the
           | "background" hidden context: training interviewers. I think
           | the "foreground" discussion point ("please make me a car") is
           | way less relevant than the skill of an interviewer.
           | 
           | For about a year I tried this with every candidate: literally
           | tell them what I am looking for in the interview and what is
           | on my scorecard. Nobody asked any questions even once.
        
         | quietbritishjim wrote:
         | The problem with that is you might feel pressured to talk about
         | Prius extends Car extends Vehicle etc. even if you wouldn't do
         | that in real code because it seems the question is meant to
         | probe the concept of inheritence so it's natural to answer that
         | way.
         | 
         | It takes a brave interview candidate to say "the best solution
         | depends on the exact nature of the problem - and your problem
         | doesn't make any sense so there's no valid solution".
        
           | BoorishBears wrote:
           | I feel like you picked up on the wrong problem in their
           | example...
           | 
           | > Prius extends Car extends vehicle extends motor extends
           | 
           | The problem isn't that it's too in-depth, or too overbuilt,
           | that answer is as close to objectively wrong as you get on a
           | pretty open ended question and I'd imagine only an extremely
           | junior developer would come up with that.
           | 
           | For example, you could maybe argue that a model of car
           | extends a specific car. I'd expect an experienced developer
           | to favor composition over inheritance at that stage, but
           | that's open to debate.
           | 
           | But if a Vehicle extends a motor, does a motor extend a
           | piston? Does a wheel extend a lug nut?
           | 
           | At that point you're moving away from "arguably correct" to
           | just plain failing to answer the question. It shows they
           | don't know the difference between composition and
           | inheritance.
           | 
           | > It takes a brave interview candidate to say "the best
           | solution depends on the exact nature of the problem - and
           | your problem doesn't make any sense so there's no valid
           | solution".
           | 
           | You're free to ask questions, we're supposed to be trying to
           | collaborate. I don't think it's "brave" to instead say "your
           | problem doesn't make sense". Shows more hubris than anything.
           | 
           | How is it any different than a systems design question?
        
           | bdg wrote:
           | > It takes a brave interview candidate to say "the best
           | solution depends on the exact nature of the problem - and
           | your problem doesn't make any sense so there's no valid
           | solution".
           | 
           | I got that a few times and it's a good sign I have a senior
           | candidate.
           | 
           | The real-world use case is simple when you imagine that I
           | want to get a JSON payload describing different car
           | configurations, possibly for a "car configurator" on a sales
           | website, or perhaps for a videogame where there are different
           | cars with different attributes at random. Once I go from
           | "make a car" to "give me some json" everyone started to see
           | it's as a less fictitious problem.
        
         | system16 wrote:
         | "Car/Animal extends..." slowed my ability to comprehend OOP
         | significantly when I was learning to code. It just did not make
         | any sense to me and I felt like an idiot for not being able to
         | equate these "real-world" examples to the code I was trying to
         | write. I just could not make the connection.
         | 
         | It wasn't until I started trying to make a simple video game
         | that these concepts made sense: an "Enemy" class could have
         | properties of health and speed that a "Boss" or "Minion" could
         | inherit from, etc.
        
           | redisman wrote:
           | Even in games composition is much better and what pretty much
           | every engine forces you to use these days. It's a lot more
           | flexible too as you can compose a new boss out of ten
           | different components rather than having to refactor a crazy
           | tree of nonsense
        
         | praptak wrote:
         | I think it's a bad question, just like every single request to
         | "model real world things X,Y,Z for no reason" and the article
         | explains this:                   You can't add code to ducks.
         | You can't refactor ducks.              Ducks don't implement
         | protocols.
         | 
         | If I got asked this question I'd probably spend the whole
         | interview inquiring about the purpose of the modeling. It's for
         | a simulation? What info do you want to get from the simulation?
         | It's for an ERP app?
         | 
         | I don't have problems with simple tasks which are not real-
         | world but abstract something from the real-world use cases. The
         | problem with "model me a car" is that it abstracts nothing.
         | 
         | Also for the most real-life purposes of modeling a car factory
         | you wouldn't even have a "Car" in your object hierarchy.
        
         | ncmncm wrote:
         | Yes. This is why "default virtual" is such a dumb idea.
         | 
         | Virtual functions are about implementation: a derived type that
         | overrides a virtual function does so specifically in order to
         | deliver a specialized implementation.
         | 
         | The degree to which your class's virtuals match its public
         | interface is an exact measure of _how bad_ that interface is,
         | as an OO abstraction. If your class was doing enough work to
         | earn its keep, it would be presenting an interface in terms the
         | client wants to see. Those are implemented for variant internal
         | representations by composing calls to one or more private
         | virtual interfaces.
         | 
         | The above is true about OO subsystems. But not all uses of
         | virtual functions are about OO. Virtual functions are a
         | mechanism. Anywhere the mechanism is useful, it is OK to use
         | it, OO or no OO.
         | 
         | Java offers no other organizational facilities than OO
         | mechanisms, so there you have little choice but to use them
         | everywhere. In problems not suited to OO solutions, use of
         | virtual calls may have nothing to do with OO, and none of the
         | OO rules need apply: if it works, it works.
         | 
         | Any language that offers only one kind of abstraction is a very
         | poor and limited language. Calling it "pure" should not fool
         | anybody. The world is complicated and demands many kinds of
         | tools. Any one will only match certain aspects of certain
         | problems.
        
       | bborud wrote:
       | The book "Design Patterns: Elements of Reusable Object-Oriented
       | Software" has an initial chapter that provides one of the most
       | concise introductions to OO I've read. Its main focus is on
       | polymorphism and it provides some easy to understand examples.
       | 
       | In fact, if you think of Go as an OO language (which I guess many
       | won't), it has all the things you actually care about from OO. It
       | doesn't have inheritance and you don't explicitly declare that a
       | type implements some interface ( _). Since there is no
       | inheritance of implementation you are forced to use composition,
       | which is what people tend to end up recommending after a few
       | years of fighting with OO code that has been written by people in
       | love with a Linnean hierarchies and complex approaches to DRY.
       | 
       | (_) There are tricks to accomplish this, for instance creating a
       | New() function that returns an interface type, which will make
       | the compiler ensure you implement the interface).
        
       | skitter wrote:
       | > Penguins don't implement the "fly" method that can be found in
       | birds.
       | 
       | Yep. That would be like having immutable lists which implement
       | List which has an "add" method. Obviously that would be too
       | stupid for a sensible programming language.
        
       | chriswarbo wrote:
       | I completely agree that "good design"/"bad design" depends on the
       | problems we're trying to solve, rather than some abstract
       | property of the entities involved (i.e. whether "Car" should
       | extend "Vehicle" or not, and whether those classes should even
       | exist, cannot be answered without knowing what we're trying to
       | accomplish).
       | 
       | The suggestion of GUI widgets is certainly better and more
       | concrete; although it's skating dangerously close to "Circle
       | extends Ellipse" and "Square extends Rectangle" territory! (e.g.
       | https://softwareengineering.stackexchange.com/questions/2381... )
        
         | joshka wrote:
         | Much agreement on this. It's concrete, visible and has
         | behaviors that work well (click, resize, draw etc.) as well as
         | providing a good entry into the thought process when designing
         | modern user interfaces with components.
        
       | everyone wrote:
       | I'm a game dev and usually program in C#. I love C# and honestly
       | some features of OO (like objects and classes) are _great_ for
       | programming games.
       | 
       | But inheritance is just a shit feature imo. Situations where it
       | would be beneficial are _extremely rare_. I generally just
       | pretend it doesnt exist. There are much better alternatives to it
       | for most situations (eg. composition, build a class from other
       | smaller classes that do one thing)
        
         | bowsamic wrote:
         | Inheritance is just a total mess. Overriding a method,
         | especially a non-abstract one, feels more like monkeypatching.
         | It feels unsafe, it's not clear if I should do it, etc. A lot
         | of Apple APIs mark which methods should be overridden and which
         | shouldn't be. It just generally feels like a crappy approach.
         | Languages with deep inheritance trees like Ruby and Smalltalk
         | and even Objective-C are also extremely difficult to learn how
         | to use due to how much basic functionality of the object comes
         | from far up the chain. Remember when Steve Jobs wanted people
         | to make companies that just sell objects? Sounds like my worst
         | nightmare. I don't see why Rust's dynamic traits, or just duck
         | typing, aren't totally superior
        
           | fabian2k wrote:
           | The user you responded to uses C#, you can't override
           | arbitrary methods there. Only methods explicity declared as
           | virtual can be overriden, so this is different than many
           | other OO languages.
           | 
           | I think this is also the better approach, making inheritance
           | something you have to explicitly design for instead of a way
           | to monkeypatch almost everything.
        
             | smcl wrote:
             | > Only methods explicity declared as virtual can be
             | overriden
             | 
             | You can with "new", no?                   public class Foo
             | {             public void Say() {
             | Console.WriteLine("Foo!"); }             public void
             | Think() { Console.WriteLine("I;m thinkin about thos
             | beans");         }              public class Bar : Foo
             | {             new public void Say() {
             | Console.WriteLine("Actually, Bar!"); }         }
             | 
             | So with Bar we have inherited Think(), but have overridden
             | Say(). Ok technically the MSDN docs will say that we've
             | "hidden" the inherited method Say(), but the effect here is
             | that we've overridden something not marked "virtual"
             | 
             | edit: Oops yeah I'm talking rubbish, see replies for more
             | info :D
        
               | fabian2k wrote:
               | Using new is still very different from using override,
               | there are real differences in behaviour there. Though I
               | never used "new", it's usually described as a really bad
               | idea unless you know what you're doing, I never saw the
               | need to use this.
        
               | bartwe wrote:
               | Isn't that shadowing instead of inheritance ?
        
               | A321321 wrote:
               | No, that's not how new works. Any method in the base
               | class will still call the "hidden" method.
               | 
               | https://dotnetfiddle.net/zcVPof
        
               | smcl wrote:
               | Ah yeah you're right :)
        
             | bowsamic wrote:
             | Wow that's very interesting, and much better
        
         | the_other wrote:
         | Once you've ditched hierarchy, haven't you entered "functional
         | programming" territory? It's just types,
         | structs/maps/dictionaries, factories, and modules/closures now,
         | right?
        
           | rkangel wrote:
           | You've still got the concept of data hiding and an object
           | lifecycle, I think. You're right in functional programming we
           | often operate in quite a similar way to "objects without
           | inheritance" - your module has an internal data structure,
           | creates it at some point, manipulates it through a series of
           | module functions. of course in functional you are usually not
           | usually mutating directly.
        
           | everyone wrote:
           | The atomic element of code is the function. If I just
           | _religiously_ stick the fundamentals of good programming,
           | _tiny_ functions, avoid coupling, avoid duplication (within
           | reason), then I am at least half way to having nice
           | maintainable code already.
           | 
           | Once I have all these really nice tiny functions, Its trivial
           | to reorganise and replace them.
           | 
           | Though I find classes also very useful, a class is mainly a
           | bucket that you put functions into. Ideally a class is a
           | black box that does _one thing_. All the functions needed to
           | do that thing are in the class and also any relevant fields.
           | Not being able to have fields in there would be counter
           | productive. It 's just so handy to have all the functions +
           | fields that govern this one element of your game all in one
           | place.. When you need to make changes everything relevant is
           | one place, and that place also functions as a black box that
           | the other elements dont know about. So to change one feature
           | in the game u just change or replace that one class.
           | 
           | Just looking at the functions, nice maintainable code would
           | look identical in a functional or OO language. I think the
           | big differences between functional and OO are actually not
           | important. I could put functions in a class, or just together
           | in the same file. Putting certain functions and fields into
           | this same bucket is just a convenience and its intuitive to
           | understand imo. Like I said in 6 months time when I have to
           | change feature X I know where to look.
        
           | fabian2k wrote:
           | You still have classes, even if you ignore inheritance
           | entirely. And e.g. with the built-in dependency injection in
           | ASP.NET it feels like you're subtly encouraged to use
           | composition for many cases. This is still very much OO-style
           | programming, inheritance is only one part of this.
        
       | layer8 wrote:
       | The problem is "A extends B". You want to teach "A implements B"
       | first. The important idea is that there's a protocol/interface
       | for which there can be different implementations used in parallel
       | at runtime, and the interface type allows the client code to
       | abstract from the different implementations. The next step is
       | that an implementation may implement multiple interfaces at once,
       | which also may be subinterfaces of one another. Only as a last
       | step you can introduce implementation inheritance, which is just
       | an optimization (both in runtime mechanism and code verbosity)
       | when reusing code, an optimization which comes at a cost
       | regarding maintenance and implementation dependencies.
        
       | quietbritishjim wrote:
       | Perhaps this would work: Build a program that...
       | 
       | * That has a hardcoded list of strings baked into it
       | 
       | * Accepts as inputs a filename (but can be empty) and a substring
       | to search for
       | 
       | * At runtime, either (a) (if filename empty) iterates over
       | hardcoded list of strings, or (b) opens file and iterates over
       | lines in it
       | 
       | * For each string / line, if contains search string then output
       | to stdout (and maybe also increment a counter).
       | 
       | It's very artificial but does allow the basic idea of OO to be
       | illustrated and it's far less code than a GUI example. You have a
       | base class and two derived classes that really do have a
       | different mechanism, and even have different state (file handle
       | or integer index). The program logic is also quite decoupled (the
       | class interface is not tied to the fact you're doing string
       | search).
       | 
       | You could solve the problem without classes by reading the whole
       | file into the same list structure as the hardcoded strings, but
       | that aspect is actually quite nice because you can present that
       | first and then ask what to do if you want to avoid that
       | unnecessary memory usage. It's also nice that you're illustrating
       | a really common OO pattern (iterator).
       | 
       | The only slight downside is that some languages make this easy to
       | do without (explicit) classes e.g. Python generator functions.
       | But you can add a footnote that they exist and this is just to
       | illustrate classes - it still beats the abstract examples.
        
         | Joker_vD wrote:
         | Refactoring something along the lines of
         | static void Main(string[] args) {             var filename =
         | args[0];             var needle = args[1];             var
         | linesToProcess = filename != '' ? File.ReadAllLines(filename) :
         | HardcodedLines;             ProcessLines(linesToProcess,
         | needle);                 }              static void
         | ProcesLines(string[] linesToProcess, string needle) {
         | // lines-processing code...         }
         | 
         | into something like                   static void Main(string[]
         | args) {             var filename = args[0];             var
         | needle = args[1];             var linesProvider = filename !=
         | '' ? FileLinesProvider(filename) :
         | MemoryLinesProvider(HardcodedLines);
         | ProcessLines(lineProvider, needle);         }
         | static void ProcesLines(ILinesProvider linesProvider, string
         | needle) {             var linesToProcess =
         | linesProvider.GetLines();             // lines-processing
         | code...         }
         | 
         | would still _need_ some justification, IMO: what 's the benefit
         | of giving ILinesProvider to the ProcessLines instead of just
         | string[]?
        
           | [deleted]
        
           | quietbritishjim wrote:
           | I'm not clear on what you GetLines() method is returning
           | (thanks to the var keyword - very useful but sometimes
           | problematic, this is one of those times).
           | 
           | If your GetLines() is returning a string[] then I agree there
           | was no benefit. But that's not what I meant. As I said, I'm
           | imagining the base class (ILinesProvider) to have an
           | iterator-like interface. So, rather than string[] GetLines()
           | method, it would have a string getNextLine() method that you
           | call in a loop. That way, with the file, you don't load the
           | whole thing in memory (as I also said).
           | 
           | If your GetLines() method is returning an IEnumerable<string>
           | (since this seems to be C#) then this is the problem I
           | mentioned at the end of my comment - the base class I'm
           | imagining is similar to an existing base class in the
           | language, and it's confusing to write your own similar-but-
           | slightly-different version in an example. But I think it's
           | best to do it anyway and explain it away in a footnote. (But
           | my ILinesProvider wouldn't return an IEnumerable, like in
           | your snippet - instead, ILinesProvider actually _is_ the
           | iterator (but with a slightly different interface).)
        
       | jacobsenscott wrote:
       | OOP != class hierarchies. Unfortunately when the industry shifted
       | from procedural programming to OO they chose C++ and Java. These
       | languages are mostly procedural languages with classes bolted on.
       | They pretty much forced you to write hierarchies - it was the
       | only way to get the good parts of OO - loose coupling,
       | polymorphism, inversion of control, etc. Since inheritance was
       | the only tool in the toolbox industry programmers naturally were
       | driven to try and leverage inheritance for everything even though
       | it is the worst part of OO.
       | 
       | Modern multi-paradigm langues do a much better job of letting you
       | use OO when OO works, functional when functional works, etc.
       | 
       | You should use the tool that works when it works. I do plenty of
       | functional and OO programming in ruby, declarative programming in
       | SQL, mostly functional style in JS. There are plenty of places
       | where each one works great.
        
       | kabdib wrote:
       | I spent a LOT of time in the late 80s and the 90s wrestling with
       | class hierarchies, coming up with one tower of abstraction after
       | another. So much wasted time; perhaps it didn't help that my
       | vehicles were C++ and Java, but I think that I would have fared
       | just as well in any other OOPy language.
       | 
       | What I got as a side-effect was a bunch of re-written code. And I
       | wondered:
       | 
       | Maybe OOP is a _terrible_ paradigm. Maybe the designs that come
       | out of it are so awful that it causes people to rewrite code,
       | over and over. Re-examined systems are often better than first
       | attempts. Once you reimplement something five or six times, you
       | 've probably explored most of the problem space and are pretty
       | good at it. It's even likely that you are _thoroughly sick_ of
       | the project and just want to get the stupid thing done and
       | shipped. (Yeah, your PMs and entire management chain just smiled
       | behind your back).
       | 
       | In other words, it's Brook's "plan to throw one away, you will
       | anyway" in action, while telling your boss that you had to
       | rewrite your framework _again_ because it didn 't handle the
       | flightless, swimming bird scenarios.
        
       | nmilo wrote:
       | Obviously modelling your program like Car extends Vehicle is bad,
       | but thinking in terms of real-life relations makes thinking about
       | complex subtyping relations like covariance much easier to grasp.
       | 
       | Compare:
       | 
       | > If B and C are subtypes of A, then List<T> must be invariant
       | over T since List<B> should not be a subtype of List<A>, which
       | can contain both B and C.
       | 
       | with:
       | 
       | > List<Cat> can't become List<Animal>, because you might add Dogs
       | to that list!
        
       | dang wrote:
       | Related:
       | 
       |  _Goodbye, shitty Car extends Vehicle object-orientation tutorial
       | (2011)_ - https://news.ycombinator.com/item?id=21298341 - Oct
       | 2019 (101 comments)
       | 
       |  _Goodbye, shitty "Car extends Vehicle" object-orientation
       | tutorial_ - https://news.ycombinator.com/item?id=2914405 - Aug
       | 2011 (128 comments)
        
       | skadamat wrote:
       | Object oriented programming was meant to be used in a Smalltalk
       | environment, where immediacy & liveliness were key principles of
       | the design.
       | 
       | OOP wasn't meant to be used by professional programmers ever, but
       | this is always always forgotten. Why do we still talk about OOP
       | when it comes to professional / full-time programming? OOP was
       | always meant to be about providing end-users a way to create
       | programs & dynamic behavior without becoming full-time
       | programmers.
       | 
       | - http://userpage.fu-berlin.de/~ram/pub/pub_jf47ht81Ht/doc_kay...
       | 
       | - https://wiki.c2.com/?AlanKaysDefinitionOfObjectOriented
        
         | pbourke wrote:
         | I don't get this originalist argument. After it's introduction,
         | OOP very clearly evolved in a professional direction almost
         | immediately. Why would you erase that history, and the work of
         | hundreds to thousands of people working on object technologies,
         | because it wasn't the original conception?
        
           | skadamat wrote:
           | Fair point! I guess when I see people saying "OOP isn't
           | getting us what we want" ... then I want to point out that it
           | was adopted for a different use case for which people are
           | claiming it's falling short for?
        
       | Ensorceled wrote:
       | As someone who has been using OOP since C++ was Cfront and you
       | fetched it from usenet ... I find these conversations
       | fascinating.
       | 
       | It's like listening to a conversation about cars where someone
       | says they hate cars because you have to figure out a manual choke
       | and the many inner tube failures.
       | 
       | Do people still use these crazy inheritance trees? I haven't
       | encountered it since the early 2000's.
        
         | jansan wrote:
         | Does this count?                   EventTarget -> Node ->
         | Element -> SVGElement -> SVGGraphicsElement ->
         | SVGTextContentElement -> SVGTextPositioningElement ->
         | SVGTSpanElement
         | 
         | And the whole tree could be described as "crazy", but it does
         | actually make sense.
        
         | aidenn0 wrote:
         | From an outsider's perspective, they do seem to still be
         | popular in Java land.
        
       | King-Aaron wrote:
       | You can add code to ducks, however the results may vary from your
       | expectations
        
       | snorkel wrote:
       | When I first learned OOP from the dry examples like Shape is the
       | base class of Circle and Triangle, and so on ... I just didn't
       | appreciate why that was much better than the flat imperative
       | style of plain old C and retreated back to the comforts of C.
       | Then I started writing larger C programs with structures that had
       | common function pointers called "draw" and "rotate" and realized
       | these are just obj ct methods ... and the structures were
       | different implementations of a common base class ... OK, so I
       | guess OOP is useful after all.
       | 
       | So it helps to compare the old ways to the new ways side by side,
       | with a real world example, to understand why the new ways are
       | better.
        
       | alangibson wrote:
       | > In good OO programming, we don't make class hierarchies in
       | order to satisfy our inner Linnaeus
       | 
       | I wish someone had told me this when I first started coding. I
       | wasted so much time building pointless hierarchies based on
       | ontology rather than DRY.
        
         | legulere wrote:
         | DRY comes from a time where you had people not using
         | abstractions at all. I have seen code from the 80s where code
         | for printing forms were one function each writing directly what
         | gets sent to the printer for each form with no abstraction at
         | all. DRY was meant for people that wrote that kind of code. If
         | all you have ever come into contact with is normal modern code,
         | than DRY goes overboard: you start to abstract too much and
         | make the code harder to read and maintain.
         | 
         | The real art is maintaining the balance between abstraction and
         | pragmatic simple code.
        
         | a4isms wrote:
         | The thing about the natural world is that "Nature does not
         | practice OOP." Our nice hierarchies of animals reflect our
         | desire to build hierarchies. Those hierarchies are tools that
         | are useful for many purposes, but they weren't blueprints for
         | constructing life.
         | 
         | I speculate that if we think of our hierarchies as tools, they
         | say at least as much about our own brains and the problems
         | we're trying to solve as the do about the domain.
         | 
         | Which is fine, tools exist as levers for the mind. But we
         | shouldn't confuse them with reality. _The map is not the
         | terrain_.
        
           | jhbadger wrote:
           | I disagree. Nature does practice OOP and taxonomic
           | hierarchies are real things in nature if they are based on
           | their evolutionary history. People say penguins are
           | "flightless birds", but look how they swim. They don't swim
           | like fishes. They basically are flying in water because they
           | share the same genetic programming as other birds, just
           | slightly modified to use a different medium. Nature doesn't
           | re-implement things from scratch but is great at code reuse.
        
             | igouy wrote:
             | Nature doesn't do design, period.
        
               | jhbadger wrote:
               | True but irrelevant. The fact is nature "subclasses" (in
               | the OOP sense) animals all the time in evolution even if
               | it is an unintelligent process ultimately due to chance
               | modification.
        
               | igouy wrote:
               | > The fact is...
               | 
               | Perhaps you mean -- The analogy is...
        
               | Jcowell wrote:
               | I think I understand what your saying. In this context a
               | penguin is _implementing_ a quality it's inherited from
               | the gene it has as a bird. A Penguin may not be a _bird_
               | in the same way we may thing a _fly_ method should work ,
               | but it's _fly_ method derived from the fact that it's
               | genetically a bird.
        
         | bborud wrote:
         | DRY is also not the goal.
        
           | rkangel wrote:
           | It's not a million miles from it though. If you start from
           | DRY as an approach to OO design you'll produce something
           | vaguely reasonable and be close enough to where you want to
           | be that you can learn the differences.
        
             | bborud wrote:
             | I should have been a bit more precise. There are essentialy
             | two ways to approach DRY in OO. One is to inherit
             | implementation, which isn't the best way to go about things
             | since it leads to more brittle code. The other way is to
             | make sensible use of polymorphism and composition.
             | 
             | There are aspects of DRY that are worth pursuing (like
             | reducing the number of places you need to get things right)
             | and some that aren't worth pursuing (thinking you reduce
             | the amount of work by re-using code heavily). For code to
             | be reusable you have to be sure that developers only have
             | to care about its interface. Implementation inheritance
             | often doesn't do that. I've worked on projects with people
             | who were obsessed with DRY and made heavy use of
             | implementation inheritance, and the projects inevitably
             | needed extra work to untangle a brittle mess with lots of
             | surprising behaviors.
        
               | rkangel wrote:
               | Yes, to be fair the simplest advice to new programmers
               | about how to use inheritance should be "don't".
               | Aggregation good, polymorphism good, inheritance bad more
               | often than not.
        
       | legulere wrote:
       | The problem with all those examples is that they are
       | specializations, but that's not really how inheritance works in
       | OOP languages and more resembles refinement types. A square is a
       | rectangle with the condition, that both sides have the same
       | length.
       | 
       | Inheritance in OOP languages is used to either for interfaces, or
       | for tacking on data and maybe functionality because you are too
       | lazy to implement out composition instead.
        
       | acidburnNSA wrote:
       | I do scientific modeling. We have classes and hierarchies of
       | different physical components we simulate, like fuel assemblies
       | and pumps and heat exchangers. I guess this falls into the
       | exception of "clone of The Sims or something" so perhaps my
       | complaint is covered.
        
         | helmholtz wrote:
         | Yep, me too. I guess most programmers dont know what modelica
         | is or does.
        
       | bottled_poe wrote:
       | Didn't read the article, but based solely on the title, I assume
       | it's a straw man. I.e. declare that vehicle is a sensible
       | abstraction for our needs, then show why it isn't.
        
       | mackman wrote:
       | Long ago in the games industry I was applying for a position at a
       | studio. Didn't end up getting a job but a friend did. A few
       | months later he was bemoaning their terrible code base which
       | included a car class that inherited from wheel four times.
        
         | hikarudo wrote:
         | "A car is a wheel, and is a wheel, and is a wheel, and is a
         | wheel".
        
       | bighuh wrote:
       | > We make class hierarchies in order to simplify the code by
       | allowing different parts of it to be changed independently of
       | each other, and to eliminate duplication (which comes to the same
       | thing).
       | 
       | Yeah. Exactly. Which is why Dog extends Animal. It has a set of
       | common features (code/behaviors, and data like number of legs,
       | amount of fur, and so on). It is a really good analogy and a
       | really good point to give someone an idea of why you would use
       | inheritance. It requires absolutely no programming knowledge, and
       | best of all, it's not difficult to imagine cases where you might
       | actually use it. c.f. basically any game with dogs in it where
       | they're a subclass of NPCs.
       | 
       | And the same for Car extends Vehicle. A vehicle can have any
       | number of wheels and any number of doors. A car can have any
       | number of doors but it always has 4 wheels. All cars move in
       | approximately the same way, while vehicles can articulate or
       | skid-steer or ...
       | 
       | Just because something is simplified does not mean it has no
       | value. And just because *you* would not use these designs in
       | *your* life does not mean others wouldn't. As TFA says: "unless
       | they are talking about writing a clone of The Sims".
        
         | czx4f4bd wrote:
         | > it's not difficult to imagine cases where you might actually
         | use it. c.f. basically any game with dogs in it where they're a
         | subclass of NPCs.
         | 
         | Except even in game design it's a bad idea once you get more
         | than a handful of entity types.
         | https://gameprogrammingpatterns.com/type-object.html
        
         | jzwinck wrote:
         | Here are a car and a motorcycle each with three wheels:
         | 
         | https://en.m.wikipedia.org/wiki/Reliant_Robin
         | 
         | https://en.m.wikipedia.org/wiki/Piaggio_MP3
         | 
         | What shall we do with them in our class hierarchy?
        
         | lelandbatey wrote:
         | > A vehicle can have any number of wheels and any number of
         | doors.
         | 
         | Does that mean zero doors and zero wheels? Because a jet ski is
         | surely a vehicle, right, and it has zero wheels and zero doors.
         | 
         | Likewise, what about a cable car (aerial lift)? It has doors
         | (maybe), but probably not an internal engine. It's definitely a
         | vehicle, but it has almost nothing that's similar about it
         | compared to the car (automobile) you described earlier. If it
         | where a class, the "vehicle class" could really only
         | meaningfully have some attributes related to transport (like
         | human/cargo capacity) and some methods around movement in the
         | most general sense. If we keep breaking this down, we'll find a
         | huge taxonomical network, a vast sea of interlocking Venn-
         | diagrams of attributes and capabilities. This is the corner
         | that those OOP tutorials force people into: trying to turn a
         | network into a tree, and it doesn't ever _really_ work (at
         | least not without awkward compromises).
         | 
         | Sure, for a beginner who's never grappled with trying to map
         | concepts to things, this can be a nice contained way to start.
         | Eventually though, that beginner will need to learn some the
         | _next_ levels of more general thinking; e.g. one where they can
         | start modeling interfaces /protocols and their relationships.
        
       | cloogshicer wrote:
       | Does anybody have some actual _good_ examples for teaching
       | inheritance to beginners?
       | 
       | I've thought about this for quite some time and couldn't come up
       | with anything that strikes a nice balance between simple and
       | actually useful.
        
         | dls2016 wrote:
         | Any sort of plugin architecture, e.g. VST audio plugins. Often
         | there's an interface which you could implement directly, or you
         | could inherit from an abstract class which provides some common
         | functionality. I like this because it stresses that deep
         | hierarchies aren't necessary to be useful. Also, if you're
         | refactoring and see a "switch on type" then a little bit of
         | inheritance is probably OK.
         | 
         | I admit I taught intro to C++ and used shapes as an example. I
         | spent a few weeks building a little "ASCII render" thingy with
         | a 80x30 "canvas" where each character is a pixel. Then you
         | could place Shapes on the canvas and the shape would decide
         | whether a given pixel was contained() inside of it.
         | 
         | I remember my first OO class where the car analogy was proven
         | to be bad using the existence of the El Camino.
        
           | cloogshicer wrote:
           | Thanks for your suggestion!
        
       | d--b wrote:
       | If I had to state a law about how to do OOP correctly it would
       | be:
       | 
       | - A base class and its implementation(s) must never be written by
       | the same person.
       | 
       | Obviously a bit of a strong opinion, but inheritance should
       | delimit layers of abstraction, technically separating the
       | responsibility of developers.
        
         | bluGill wrote:
         | A base class should only exist where there are implementations
         | -it needs to be plural. Otherwise just fold the base into the
         | child, or better yet use composition not inheritance. I
         | generally do not count any form of test double (mock...) as a
         | an implementation, since only rarely is the real implementation
         | something that you should mock.
        
       | TomMasz wrote:
       | When you're introducing OOP to beginning programming students,
       | these metaphors still have some value. Beyond that, though, it's
       | time to go with more realistic examples of polymorphism.
        
       | scotty79 wrote:
       | Entity-Component-System is so much nicer paradigm than any
       | hierarchy. I wonder why it's not used much outside of gamedev.
        
         | rje99 wrote:
         | I would have agreed with this whole-heartedly when I first
         | started using ECS. It has its own difficulties though.
        
           | scotty79 wrote:
           | Like what?
        
         | 202206241203 wrote:
         | That's just structured procedural programming - subroutines
         | mutating separately-defined data. This used to be all the rage
         | before OOP took over!
        
         | viktorcode wrote:
         | It does not replace OOP because there are no answers to other
         | questions like: does it support polymorphism? Or data
         | encapsulation? Etc. It's a data access design principle, and
         | that's about it.
        
           | scotty79 wrote:
           | To the same degree that SQL does. The answer is, "yes, no,
           | does it matter? is that even interesting?"
        
         | torginus wrote:
         | It is - it's just called SQL and is considered the opposite of
         | cool.
        
           | scotty79 wrote:
           | It's kinda shame that people try to cram it into rigid object
           | model instead embracing flexibility on the code side too.
        
         | joshka wrote:
         | Explain "entity" to someone that is just starting their
         | software development career ;)
        
       | dymk wrote:
       | I disagree with the article, which talks about building a
       | holistic understanding when learning, and then ironically misses
       | the forest for the trees regarding programming metaphors.
       | 
       | The author is obviously an experienced programmer, so I suspect
       | they've forgotten what the mindset of an absolute novice is like,
       | and to start programming with zero prior exposure to the
       | concepts.
       | 
       | For the novice being introduced OOP, the `Duck / Cat / Dog <:
       | Animal` analogy is useful to demonstrate how things like
       | inheriting methods and sharing behavior works. It helps one start
       | to think in terms of hierarchies and the boundaries between
       | "concepts" in code. Obviously, in 'real' code, you won't be
       | writing much that involves a literal hierarchy of animal species.
       | 
       | That's not the point. A beginner knows what a dog and a duck and
       | a cat are in the real world. The "a-ha!" moment is mapping real-
       | world relationships into a brand new abstract environment called
       | Object Oriented Programming.
       | 
       | When you've never written a line of code in your life, abstract
       | things like "network protocols" and "file I/O" and "ImageDisplay"
       | (which is neither an image nor a display) are confusing, and so
       | frustrating, and so not useful for teaching a complete novice.
        
         | lamontcg wrote:
         | It is useful to talk about "Bicycle inherits from Car with
         | NumWheels set to 2" and "Boat inherits from Car with Surface
         | set to WATER and not GROUND" as examples of Liskov violation.
        
         | redisman wrote:
         | I disagree. Teaching people to think about abstractions wrong
         | is not in any way helpful. Everyone I know including myself had
         | to spend years writing bad code before realizing that this way
         | of thinking is counterproductive. It reminds me of this quote
         | from Kung Pow
         | 
         | Don't worry about Wimp Lo, he's an idiot. We've purposefully
         | taught him wrong... as a joke.
         | 
         | Composition is an infinitely better method to teach if you have
         | to teach OOP. That way you don't need to unlearn what you
         | thought was the right way but is actually useless at modeling
         | almost anything.
        
           | JoshCole wrote:
           | Abstractions are useful because they simplify and if you
           | don't allow error you don't allow maximization of
           | simplification. You can formally relate this to learning
           | problem formulation complexity. When you do you encounter
           | things like branching factors which effect solution times
           | which lead to natural results like fast but finishes being
           | better than optimal but never terminates. This can hold even
           | despite error in the abstraction and there are techniques for
           | recovering that error at runtime because a particular problem
           | is a less general and more specific situation and so isn't as
           | cursed by branching factor.
           | 
           | Edit: A previous version of this comment was more wordy in
           | stating this and mentioned that there are formal proofs to
           | this effect.
        
             | random314 wrote:
             | I don't think this constitutes a formal proof on any way at
             | all.
        
               | JoshCole wrote:
               | The formal proofs aren't in a Hacker News comment; just
               | go read game theory research papers. For example,
               | https://www.cs.cmu.edu/~noamb/papers/17-NIPS-Safe.pdf
               | 
               | Or go try and solve a moderately complicated anything
               | without allowing yourself abstractions with error. I
               | won't wait on you, because your solutions will never
               | terminate in my lifetime for even things as simple as
               | chess which is a hell of a lot simpler than reality.
        
               | JoshCole wrote:
               | Take the simplest problem of searching over a continuous
               | range between zero and one exhaustively. Let the value
               | you search for be 0.1. By a diagnolization argument we
               | can realize that even a search carried on for an infinite
               | amount of time and granted an infinite amount of space
               | would not terminate. After all, to find 0.1 you need to
               | make it past 0.01. But to reach that you have to reach
               | 0.001. And so on. Let abstraction map from N to 1 over
               | this space. As N goes up, abstraction error goes up, but
               | the problem is no longer impossible because the
               | diagnolization proof no longer holds. Let b be a
               | branching factor in a game. As b goes to infinity, the
               | game graph becomes continuous. In learning formulations
               | under the game theoretic school of thought you have to
               | search the game graph to get the average of your policies
               | proportional to your counterfactual regret. That implies
               | walking the continuous game graph. In reinforcement
               | learning the bellman equations give you your values. This
               | is also defined in terms of the game graph. So again we
               | fall prey to the problem. We've shown that walking
               | continuous things is impossible even under conditions
               | that are ideal - infinite speed and memory and we've
               | shown that this relates to the learning problem
               | formulations I spoke of.
               | 
               | Obviously this only gets worse when we impose reality -
               | we don't actually have infinite space on our computers.
               | They don't compute for an infinite amount of time either.
               | But notice that before we searched for infinite space and
               | time and we failed? When we move down to finite space and
               | finite time we still have the property of completing
               | after full enumeration. We have a finite amount of
               | computing capacity. We have a finite amount of
               | computational storage. Yet the growth rate for
               | unabstracted game trees is exponential. Let c be our
               | constraints.
               | 
               | An abstracted game maps n states to one state. So it has
               | log_n(X) where X is your state count. An abstracted game
               | has X states. Since log_n(x) < X we know there exist
               | terminating algorithms for abstraction that are not
               | terminating for unabstracted because log_n(x) < X when X
               | = c. So we get log_n(X) < c when X = c.
               | 
               | This is actually _much less_ than the real world gains.
               | Since in learning we get the policy expectations multiple
               | times over the game graph and the convergence guarantees
               | relate to the complexity of the graph you get a much more
               | worthwhile window than just the difference of log_n(X)
               | versus X. For much tighter bounds check out game theory
               | research. They get error bounds on the abstraction error
               | too by choosing clustering solutions with provable
               | properties. So it really has a much stronger formal
               | treatment than you might imagine.
        
           | moron4hire wrote:
           | I'm so sick of this "composition is so much better than
           | inheritance" talking point that seems to have become dogma in
           | the last 5 years. You use both. They are completely different
           | things.
           | 
           | Look at a classically inheritance-based system: a GUI
           | library. You'll see lots of inheritance. Buttons and
           | Checkboxes extend ClickableThing, ClickableThing extends
           | Control. Whatever.
           | 
           | And then you _compose_ those controls on a Form.
           | 
           | Look at a classically composition-based system: a video game
           | entity-component system. You'll see Monsters composed of
           | GraphicsObjects and Animations and WeaponSlots and AIAgents.
           | 
           | And you implement all of those components in an inheritance
           | hierarchy, or else your GameObjectThinger can't have a list
           | of them that can grow or shrink at runtime.
           | 
           | So keep saying "teach composition, not inheritance". You
           | might as well tell musicians to study "rhythm, not tempo".
        
         | czx4f4bd wrote:
         | The problem here is that that "a-ha!" moment is completely
         | fallacious and not actually helpful. You _think_ you 've gained
         | some deeper insight, but any attempt to apply that
         | understanding is all but guaranteed to result in pain and
         | frustration. The mental model that OOP objects map directly
         | onto real physical objects plagued me for years, because I
         | could not understand how I was supposed to translate it into
         | real code.
         | 
         | I finally broke through it after being forced to write a ton of
         | enterprise Java, which helped me come to realize that classes
         | and objects are useful units of abstraction for encapsulating
         | related sets of data and behavior, as well as finally grasping
         | the idea of composition and the utility of things like
         | dependency injection, but I could've avoided years of pain if
         | someone had actually explained that properly with concrete
         | examples of useful object-oriented code. Since then, I've seen
         | the same conceptual misunderstandings over and over from
         | beginners and all of it comes down to the fact that these inane
         | `Cat extends Animal` examples are simply wrong.
         | 
         | > When you've never written a line of code in your life,
         | abstract things like "network protocols" and "file I/O" and
         | "ImageDisplay" (which is neither an image nor a display) are
         | confusing, and so frustrating, and so not useful for teaching a
         | complete novice.
         | 
         | The solution here is not to teach them something that dooms
         | them to even more frustration and confusion in the future.
         | Also, if someone has never written a line of code ever, why are
         | you teaching them OOP? If someone can't yet grasp concepts like
         | network protocols and file I/O, OOP is literally not useful to
         | them. It's like teaching someone calculus when they haven't
         | even learned algebra yet. Start with the basics and then build
         | up from there.
        
           | glowingly wrote:
           | > It's like teaching someone calculus when they haven't even
           | learned algebra yet. Start with the basics and then build up
           | from there.
           | 
           | I mostly agree in principle with the idea of building up a
           | foundation. Going right into the deep end without decent
           | guidance may leave one with horrible heuristics.
           | 
           | However, (IMO) a crawl->walk->run abstraction does not always
           | work well. I hate to say "it's case by case," but pedagogy is
           | not easy. Algebra, IMO, should be taught either near or with
           | its uses (modern calculus being one of them). Otherwise, one
           | may be learning a bunch of abstract concepts without much
           | reification.
        
         | sumy23 wrote:
         | Class inheritance in OOP seems to be largely derived from such
         | contrived examples as "Duck extends Animal." It's basically a
         | worthless concept and OOP would be better without it.
         | Composition over inheritance leads to much better code.
        
           | lamontcg wrote:
           | You still need to extract the right components and
           | abstractions.
           | 
           | And inheritance can be expressed in terms of composition, the
           | base class(es) can be represented as composition and
           | delegation instead (with a protected interface, a public
           | interface and the implementation details all tightly coupled
           | behind a single name which is what really makes it so poor).
        
       | gmac wrote:
       | I have found survey questions to be a good, practical example of
       | the pleasures of OOP.
       | 
       | See, for example, section 3.2 in
       | https://www.econstor.eu/obitstream/10419/66819/1/717744094.p...
        
       | AutomatedChaos wrote:
       | Not sure why but this book came to mind:
       | https://en.wikipedia.org/wiki/The_Island_of_Doctor_Moreau
       | 
       | "You can't fake the ability to turn a duck into a penguin by
       | moving its duckness into an animal of some other species that can
       | be replaced at runtime."
        
       ___________________________________________________________________
       (page generated 2022-06-24 23:03 UTC)