[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)