9 ms·
Goodbye, shitty "Car extends Vehicle" object-orientation tutorial
- BenoitEssiambre 15y agoI would also add. Don't introduce a language by showing how to compute the Fibonacci sequence. I never have and never will need to compute the Fibonacci sequence. Recursion is such an infrequent part of everyday programming that it doesn't really matter if a language makes it easier.
- kragen 15y agoIt's true that it's fairly infrequent, but it doesn't follow that it doesn't matter if a language makes it easier. When it's applicable, it simplifies things enormously.
- TeMPOraL 15y agoIt is frequent in some applications, and when programming in languages that encourage you to recur instead of looping (see: functional proramming). Also, recursion is one of the most important concepts of computer science. I didn't really use recursion in my programming until I switched from C++ to Common Lisp.
- jeffreymcmanus 15y ago> You can’t add code to ducks. You do if you are creating a piece of software to track ducks. I don't get the impression that the person who wrote this ever had to seriously teach software development to software developers. Simplistic metaphors are used so frequently for this kind of thing because it prevents the learner from having to ascend more than one conceptual hurdle at a time. The alternative that he's proposing (learning inheritance in terms of some obscure drum machine software) would imply that the learner spend half their time digesting how this drum machine works, then the rest of their time (assuming they're still awake) figuring out how that relates to inheritance. It isn't helpful.
- scott_s 15y agoI agree, but he gets to a much simpler, teachable example near the end, which is representing shapes. Mathematically, a circle is an ellipse, and an ellipse is a polygon. It's much cleaner than the biology or work-place inspired taxonomy. A simple drawing program can feel less contrived, but, then again, so can simple games that use an object hierarchy unrelated to polygons. Fundamentally, though, I do agree with your point. You don't use completely realistic examples to teach concepts for the fist time. All of the incidental material that is necessary to grok the example clouds the new concepts. Sometimes this means the examples will be pedagogical toys, and that's okay. The benefit of the already known taxonomies is that the student has less to learn. The new concept is more obvious. If the worry is that the example is too much like a toy, then address that in the second example. In general, I think people put too much emphasis on first exposure to concepts. Education is iteration. If it doesn't take the first time, there are more tries coming up.
- georgefox 15y ago> I agree, but he gets to a much simpler, teachable example near the end, which is representing shapes. Mathematically, a circle is an ellipse, and an ellipse is a polygon. Well, the circle-ellipse problem complicates that example a little bit: http://en.wikipedia.org/wiki/Circle-ellipse_problem http://en.wikipedia.org/wiki/Circle-ellipse_problem.
- kragen 15y agoThe circle-ellipse problem almost never arises in real software, in my experience. It's not in the same league as the Fragile Base Class problem, or the Diamond Inheritance Problem, or the Schema Upgrade Problem, or the Ravioli Code Problem, or the problem of poor performance caused by too many layers of abstraction, or the Big Ball Of Mud Anti-Pattern. It's essentially a thought experiment. What does it teach us? Well, one lesson is that you can't design good inheritance hierarchies in your program simply by aping ontological relationships in a Platonic Ideal World, which is the main point of my article. Another lesson is that sometimes you'll discover that your program is inadvertently violating LSP because of some nonobvious interaction, and that this is a bug, and you need to fix it. A third lesson is that mutability is tricky, in particular with respect to the Liskov subtyping relation; this same problem comes up in non-OO contexts as well, if you have mutability and any kind of subtyping relation. (OCaml polymorphic variants, for example, create a subtyping relation all by themselves, even without OCaml's OO features, and that's enough to create the problem.)
- Kevin_Marks 15y agoIs Kragen making an elaborate joke about duck typing here? Penguins don’t implement the “fly” method that can be found in birds. And you don’t go around causing things to fly without knowing what kind of bird they are. (Ducks themselves decide when they want to fly, and they certainly seem to know they’re ducks and not vultures.)
- kragen 15y ago> Is Kragen making an elaborate joke about duck typing here? Not really, no. I was just saying that "fly" methods on hypothetical bird classes fail to motivate OO.
- d0m 15y agoHeh, I agree somewhat that the Duck>Animal is a bit annoying.. but this: Here’s an example that I think would be better to use instead: the `Visible` hierarchy in [Pygmusic][], which is a kind of software drum machine. A `Timer` is a horizontal strip on the screen with a stripe racing across it. A `NumericHalo` is a spreading ripple on the screen that fades. A `Sound` is a thing on the screen that makes a sound when a `Timer`’s stripe hits it. A `Trash` is a thing that deletes `Sound`s when you drop them on it. They all inherit from `Visible`, which represents things that can be drawn on the screen and perhaps respond to mouse clicks or drop events, but they do different things in those three cases. In addition, the `Trash` and `Sound`s are subclasses of `ImageDisplay`, because the way they handle being drawn is simply to display a static image, so that code is factored into a superclass. Is by far worst.. The goal of the Person or Duck is to pick a trivial example to explain some concept. Of course you're not building Sim city and that it will be totally different in real apps. Also, it's true that inheritance is often the wrong solution and could arguably be said that it create more problems than it solves. However, before understanding all the pitfalls and the why/why not of inheritance, you first need to understand what inheritance is! So, instead of shooting someone who give an example with a Duck to explain OO, let just say that this tutor should say beforehand "Let's take a trivial example to understand the concepts of OO. Later on, we'll see why this is not always the preferred solution."
- kragen 15y agoProbably if you have a trivial piece of code, you don't need inheritance to help you structure it. But you're right that we need a smaller example than the entire class hierarchy of Pygmusic. What do you think would be the smallest sensible example? If your objective is only to explain what inheritance is rather than why someone would use it, you could probably write two classes and three methods: class A: def z(self): print "z " + self.y() def y(self): return "Ay" class B(A): def y(self): return "By" A().x() B().x() But I really think that most people will find a program easier to understand if it's not completely abstract like that — if it reflects some kind of intentional process where a programmer was trying to achieve something. But in order to explain inheritance in such a context, you need a program where inheritance makes things better instead of worse. What do you think is the simplest possible piece of code that would do that? I am thinking of a very-much-cut-down version of Pygmusic with only two drawable objects.
- kenjackson 15y agoI actually think these are really good examples. I especially like the shape example, because you quickly run into problems where you can talk about the difference between modeling for a solution and representing an abstraction.
- mcphilip 15y agoThe only situation where I could see not wanting to hear the car extends vehicle example of inheritance is in an interview where you want the candidate to give a practical example of inheritance. I see nothing wrong with using an example even a child could understand as a first stab at explaining inheritance. Fairly annoying how quickly a mostly useless rant such as this has jumped to the top of HN.
- silverlake 15y agoDepends on the audience. If you're using the "Car : Vehicle" example, you're probably explaining the basics of OO to a newbie. I think that's a fine example. If you're talking about more advanced topics, then you can use a more advanced example. In fact, you can use different examples suited to different topics! Relax, there's no need to be a pedant.
- Goladus 15y agoHonestly, when I was a newbie, the simplistic examples drove me crazy because I didn't get the point. I can solve the toy problem effectively with concepts already introduced (functions) why am I burdening my code and my brain with all this new overhead? The OP's suggestion to use a GUI interface is much better. GUI objects like windows are a common and interesting subject. Ironically, Object Oriented features in languages like Java and C# made a lot more sense to me after writing a Win32 gui app in straight C.
- therandomguy 15y ago"Car extends Vehicle" is a perfectly fine example to introduce someone to OO programming. That's all.
- deleted 15y ago[deleted]
- siglesias 15y agoI completely agree with this. Duck-laden object oriented programming tutorials made no sense as I tried to transition from C to Objective-C. The best explanation I found was Chapter 2 of Apple's "Object-Oriented Programming with Objective-C": Every object has both state (data) and behavior (operations on data). In that, they’re not much different from ordinary physical objects. It’s easy to see how a mechanical device, such as a pocket watch or a piano, embodies both state and behavior. http://developer.apple.com/library/mac/documentation/cocoa/conceptual/OOP_ObjC/OOP_ObjC.pdf http://developer.apple.com/library/mac/documentation/cocoa/c...
- deleted 15y ago[deleted]
- thaumaturgy 15y agoWay back when I was first learning OOP, the typical "car extends vehicle" examples caused me not to see the practical point of OOP for a long time. I still resist using object-oriented approaches except in cases where the code demands it. (A feature, not a bug?) And, these days, if I do use OOP in places in the code, it's for encapsulation -- which is only a style consideration -- so the "car extends vehicle" example still doesn't apply. I think a simple example from GUI programming would do much better. How about window classes? I did something like this recently: class Window { public method show() public method title(newTitle) public method close() } Easy enough. What if I want a modal window though? Almost everything in it would be the same as a regular window, except I might need to do some things slightly different for show()ing and close()ing the window. Aha! A perfect example of an appropriate time to create a subclass: public class ModalWindow extends Window { public method show() public method close() } Programming tends to attract practical individuals. I think teaching "car extends vehicle" then is usually going to cause one of two results: either a student that then uses that approach all the time, even in cases where it's not warranted, because they don't understand when it's appropriate and when it's not; or a pragmatic student that resists using it because the given examples don't apply to anything that they're actually going to have any chance of working on.
- georgieporgie 15y agoI think a simple example from GUI programming would do much better. How about window classes? It's funny, because OOP came my sphere of awareness around the same time that desktop software was beginning to use it for extensible GUIs. I'm talking about Turbo Pascal's Turbo Vision, and whatever Turbo C++ had. That actually slowed down my comprehension of OOP. I thought OOP was for GUI work. OOP added so much complexity to the GUI code, and the GUI code added so much complexity to the OOP concepts, that it actually set me back by quite a bit! :-) Mind you, it didn't help that, just as I was grasping crucial OOP concepts (e.g. a pointer to base class can point to an instance of child class), my instructor told me that was wrong and that I didn't understand OOP...
- thaumaturgy 15y ago
- joe_the_user 15y agoJust one piece in the whole object orientation blows but still is the best thing we have to make complex systems understandable to simple people quandary. It is true that the real world object examples confuse everything that OO is really used for. It is also true that we need them to make OO approachable at all. OO is essentially an ontological cluster-fck where multitudes of logical and representational levels are trampled on. The thing is, OO uses* the adhoc mixture of logical confusions that is most people's description of their world. Somehow people's amazing brains muddle through speaking natural language despite it's terrible muddling of everything. Thus we can stand the confusion of not really knowing whether "class window" is a glowy thing on the screen, a location in memory or an abstract datatype.
- guelo 15y agoI fail to see the problem. I would bet that there are tons of real games that actually have a Car class that extends Vehicle.
- AltIvan 15y agoIMHO the correct example should be something like: Project Duck is composed by classes Heart, Brain, Lungs... etc.
- georgieporgie 15y agoIf I understand correctly, this rant is about the use of simplistic examples in teaching OOP, correct? i.e. it's not just about using such examples when debating language features. If so, then I very strongly disagree with this guy. I have painful memories of trying to come to grip with OO in high school in the early 90's. Sure, those zoological and automotive examples are almost insultingly trivial, but that's because you don't need real-world engineering complexity interfering when you're just trying to wrap your head around inheritance and extension! Bird ==> Duck makes sense because they're classes. As in, those are classes of animals in the real world, and there is a relationship among them. Expressing that relationship in code doesn't mean you're coding animals, it means you're modeling a real-world class hierarchy with your data definition. I think that people are forgetting that OOP is actually hard. If you don't already have a very strong grasp on the underlying mechanics of a language (e.g. function pointers and vtables in C++), then it's going to always seem like magic.
- kragen 15y agoI appreciate your disagreement, and I might be wrong. My concern with those examples is not that they're trivial but that they're misleading.
- digitist 15y agoYou sir, are an idiot.
- SingleShot 15y agoI disagree with the arguments in general and find that modeling real world, common items is much more illuminating from a teaching/learning point of view. It would be an ridiculously long post to explain why, but here is an example picking one of the author's bullet points: "Penguins don’t implement the fly method that can be found in birds." Should bird even have a fly method? Only if all birds fly. And the application will have penguins, so the answer is "no". This doesn't make the Duck extends Bird example a bad thing though. It points out that if you do shallow domain analysis, you will end up with a poor OO design where you shoehorn things like "fly" into penguins for example. If some birds fly and some don't, some swim and some don't, some burrow and some don't, etc. you might implement this with "mix in" interfaces like "Flyer", "Swimmer", "Burrower", etc. This is a good lesson in how one can distribute different behaviors across a class hierarchy without polluting the common base interface of the hierarchy (i.e. without forcing all birds to fly). However, this burns the behavior into the concrete classes, prevents things like modeling the fact that baby (or dead) birds cannot fly/swim/burrow, and does not allow for sharing different fly/swim/burrow implementations across bird implementations (in languages like Java anyway). In other words, it shows why inheritance can be a good choice, and also a bad choice. A common solution to this would be to use two hierarchies - one is the bird hierarchy that all birds implement (with behaviors like "preen feathers"). The other is the behavior hierarchy that all behaviors implement. Birds "contain" a list of behaviors. Code that operates on birds knows the bird interface has methods that make sense for all birds. Code that operates on behaviors like fly/swim/burrow would have behaviors passed in, or perhaps ask the animal in question "do you know how to fly?" and if yes, ask for its fly behavior. There are many variations on this, but the point is that there is a separation of concerns: bird concerns and behavior concerns. This helps teach the trade-off between inheritance and containment (e.g. behaviors not burned in through inheritance can be added/removed over time, for example, when a baby bird learns to fly). The point though is that this kind of example (Duck extends Bird) helps one understand how to model real world items so they match the domain, to learn about separation of concerns, learn the pros/cons of inheritance/containment, design patterns, and perhaps most importantly, do so using concepts most people will more readily understand (cars or birds or people).
- azov 15y agoIndeed. The example with Birds, Ducks, and Penguins is much more colorful and fun then example with GUI widgets. It sticks. Students will remember ducks & penguins, but fall asleep before you even finish describing that boring GUI system. Plus, nothing prevents you from using the same fun example to illustrate the second point. E.g. you can show that while penguin is a bird in real world, in your application Penguin might actually inherit from Pinniped, because it behaves like one (and it's "behaves-like", not "is-a" that we care about).
- SalmanPK 15y agohahaha
- quanticle 15y ago>One disadvantage is that it has to deal with a fair amount of arithmetic; there's lots of `/ float(n)` and `* self.rect.w + 0.5` and `self.size2/2 * (1 - (1 - age2)*2)` and the like, which I think reinforce a common misconception about computer programming: that you need to learn algebra and arithmetic in order to write programs, and that programs mostly deal with numbers. Is this really such a disadvantage? Algebra and arithmetic are hardly advanced subjects. Isn't it fair to insist that programmers know at least middle school math before they program?
- kragen 15y ago> Isn't it fair to insist that programmers know at least middle school math before they program? Certainly not! I started programming when I was 4, many years before I got to middle-school math. And there's a great deal of software in the world that has very little to do with algebra or arithmetic. It's easy to open up http://www.canonical.org/~kragen/sw/urscheme/compiler.scm.html http://www.canonical.org/~kragen/sw/urscheme/compiler.scm.ht..., for example, to a random page and see no arithmetic at all. The same would be true of https://github.com/evilstreak/markdown-js/blob/master/lib/markdown.js https://github.com/evilstreak/markdown-js/blob/master/lib/ma... if JS didn't require you to use arithmetic to iterate over arrays.
- sabat 15y agoNothing annoys me more than this kind of analogy. People who are trying to understand it can't figure out what a Duck really is in this scenario, and what it means that it's a subclass of Bird. Use simple but realistic classes as examples.
- pacmon 15y agoThe real truth being that not even one thinks the same way. Some people learn better with visuals versus some who prefer text. Some people see no problem with Vehicle->Car or Animal->Duck or whatever other ultra basic OO example. In the end what's best is whatever can make someone understand the concept.
- Emore 15y agoYes, a thousand times yes. I have never understood why dependency injection is not taught in OOP courses -- as described in the article, DI truly shows why OOP design is useful. Conversely, the "domain model"-approach commonly used probably gains some of its attraction due to the similarities to E/R database diagrams, but far from always lead to designs which are actually useful in practice.
- dustingetz 15y agoHe asks for a simpler example. How about a collections api? Start with array, then linked lists, then functioms like contains and filter and which need an iterable interface, then different types of linked list, then add hashmap where you iterate keys, then different types of map e.g. linked map. This is all data structures 101 material anyway! After this primer, a simple GUI toolkit isn't as far out of reach.
- cema 15y ago1. I absolutely agree with the linked article. My perspective is somewhat different, however. I submit that OO hierarchy should not mimic whatever properties whatever real-world objects may happen to possess. Instead, when we solve a problem and have come up with a solution, the OO hierarchy should describe the solution. A different solution, or a different problem, may easily lead to a different hierarchy of (perhaps different) objects. And these would be software objects. 2. Therefore, I suggest that a "simple" example would come from a well-known software system. Such as a window system (rectangle, button, icon, window, etc) which, either as a concrete example or an abstraction, should be easily understood by anyone with a minimal (desktop) computer experience and no programming experience.
- 5hoom 15y agoOne of the first examples of OOP I encountered was the old 'shape' hierarchy demonstration: 'shape' is a base class. 'rectangle' is a new class that inherits from shape. 'square' inherits from 'rectangle', etc. It demonstrates the ".. is a .." relationship that inheritance describes perfectly. This is a pretty common way to introduce OOP as it draws on stuff we all know intuitively. The example proposed with a bunch of drum machines concepts, trash, timers & stripes or whatever is so mind bogglingly obscure for most people that I'm sure they would have checked out long before the actual OOP lesson begins. Not sure what the point is. Oh, and btw, just because you might not ever need to represent a 'duck' object, doesn't mean that it is a stupid example. Say I write a game with a bunch of actors, I might have a base actor class, a bird class that extends that, and a duck class that extends that. Bam, legitimate use for a 'duck' object. There's probably a lot more people wanting to write "a clone of the sims" than "an intractably obscure drum-machine".
- kragen 15y ago> 'square' inherits from 'rectangle', etc. ... It demonstrates the ".. is a .." relationship that inheritance describes perfectly. Your comment is a perfect example of why this is a terrible way to teach object-oriented design: if your squares and rectangles are mutable, as they usually are in the old shape-hierarchy demonstration, you've just violated the LSP and introduced a subtle bug into your program, in the very first line of your comment. If you understood object-oriented design, you wouldn't do that. You've been tricked into thinking you know how to do object-oriented design, but your sense of competence is an illusion. For what it's worth, although I don't have any real experience teaching other people to program, I've found that animated graphics that make sounds hold the attention of nontechnical people a lot better than abstract hierarchies of Platonic geometric shapes. I haven't compared them with bird-flocking simulations.
- nuxi 15y ago> It demonstrates the ".. is a .." relationship that inheritance describes perfectly. It's rather imperfect, see the http://en.wikipedia.org/wiki/Circle-ellipse_problem http://en.wikipedia.org/wiki/Circle-ellipse_problem for caveats.
- buster 15y agoI don't get what is soo wrong about the car example, because of it i grasped the object orientation style instantly. What's easier to undderstand than this!?
- bendmorris 15y agoThis is ridiculous. I'm teaching part of a course called "programming for biologists" this fall including the part about object oriented programming. Most of my students are ecology grad students. You're telling me I shouldn't use simple animal examples, when these are exactly the types of things my students are going to be modeling?
- JulianMorrison 15y agoInheritance. Gah, why won't this bad idea die! Shared calling protocol, shared data sub-structures, fixed compile-time equivalence hierarchy, all clumped together. Go gets it right. Cut the linkage between composition and equivalence. Composition handles inclusion/merging of existing objects. Interfaces handle equivalence of objects. Interfaces can have hierarchies, but there's no one true hierarchy baked into the objects they describe.
- MarkMc 15y agoI think the best introduction to object oriented design is shown in the first chapter of Martin Fowler's book "Refactoring". He gives the example of a DVD rental shop that has to calculate the rental fee based on the type of DVD: - A New Release is $3 per day - A Children's DVD is $1.50 for three days, then $1.50 per day after that - A Standard DVD is $2 for two days, then $1.50 per day after that http://www.amazon.com/Refactoring-Improving-Design-Existing-Code/dp/0201485672 http://www.amazon.com/Refactoring-Improving-Design-Existing-...
- jrockway 15y agoThe rest of that book, unfortunately, is stuff like: "To move code from one file to another, follow these simple steps. Step 1: Delete the code from the source file. Step 2: Add that code to the destination file. Step 3: Update references to the code in (1) to point to (2). Step 4: Recompile your project and fix the parts that don't compile." What insight!
- dtreacy 15y agoTHAT'S WHY ITS A BEGINNER TUTORIAL. Get off your high horse.
- lyudmil 15y agoThe criticism being put forward isn't at all arrogant. A tutorial for beginners that teaches you the wrong thing is incredibly detrimental. It's good to point that out and fix it.
- dtreacy 15y ago"Duck extends Bird" is not the wrong thing at all. As other comments have pointed out, the justifications in the article don't apply to someone who is just starting out learning the basics of OO.
- perlgeek 15y agoI've been working on writing a programming book for quite some time now, and I can tell you that coming up with good examples is really hard. For me, a good example meets as many as possible of the following demands (in no particular order). An example should * demonstrate the feature or concept under discussion * be short - shouldn't be more than half a page to a page * solve a problem that the reader can easily understand * solve an interesting problem * not use unrelated features of the language or library that haven't been introduced yet * should avoid complexity unrelated to the feature or concept under discussion * should be as close as possible to what one would use in "serious" (ie non-teaching) programming I found that i spent about as much time searching for good examples as actually writing text or code for the book. I'm pretty happy with some of the examples (for example the chapter on grammars parses JSON, which is rather real-world, but not too complicated), some of them still fail most of the demands given above. (If you're curious, grab the latest PDF from https://github.com/perl6/book/downloads https://github.com/perl6/book/downloads -- it's still very much a work in progress).
- bjoernbu 15y agoThe example for inheritence I liked best was something like this: Number (implementing trivial exponentiation through multiplication) RealNumber & ComplexNumber implementing their specific kind of multiplication. Later on replace the exponentiation by fast exponentiation. This directly demonstrates the benefits of avoiding code duplication.The change only has to be made in one place.
- wlievens 15y agoIn my experience, there's a process where adding accuracy (and thus usually complexity) to a domain model, implies removing existing inheritance assumptions. For instance, in the trivial case an Employee is a Person, but when you go further you discover that there are all sorts of relations that need more detailed fleshing out. Is changing jobs just a call to Employee.setEmployer? That certainly feels akward. And as that process continues, you get a more complex domain model with comparatively less fixed inheritance relations. The extends keyword sortof becomes an extend object, first-class among your domain and susceptible to dynamic behaviour. But can you teach kids that in a first OO class? EDIT: care to explain the downvote? I'm quite passionate about this subject so would love to debate it.
- aufreak3 15y agoGood to remember Alan Kay's guideline here - "OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things."
- yason 15y agoThe thing is that there ought to be no trivial examples of object oriented paradigms because they are never trivial. If you're good you can create object oriented interrelations that work but end up having object charts which have that plastic, phoney kind of artificiality that makes little natural sense. If you're a god-like programmer you can come up with interrelations of code and data that make perfect sense but they're quite not always object oriented. As for OO, the best thing I've settled with is this: - Scoop up all your programming experience and first think how do you want to reuse some code or some data structures - Then you go and look up appropriate paradigms and language features that could support your case of reuse - And finally you go figure out how to best do it in a natural way in your programming language. However, I often start with basic lists and dicts and only after something becomes truly obvious and a near-universal trait, I might make a class out of it if I can better reuse code that way. An archetypical example would be something like a BaseObject that supports some protocol for init, deinit, and refcounting, for example. Everything else can adhere to that protocol. Better yet, I'll make it an interface so I can decouple the implementation of the protocol from the definition of the protocol.
- CodeMage 15y agoThe problem people have with "Car extends Vehicle" is that they expect magic. It's the same impatience we sometimes feel with little kids because they're not grasping something we've been taking for granted for decades. If you're trying to teach someone new to OOP, there is no example that will take them from their current level of knowledge to yours, in one fell swoop. You can't expect them to grapple with decisions such as "inheritance vs. delegation" when they're only now learning what inheritance is all about. So yeah, "Car extends Vehicle" and "Duck extends Bird" works pretty well at that point. You can try to cram "Penguin" and "Helicopter" and "Flyer" and "Vehicle" down your students' throats in the same lesson, but I doubt that will magically make them digest it properly. And if you can't do that with easy stuff like "Duck" and "Bird", how do you expect them to swallow "NumericHalo" and "ImageDisplay"?