Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> 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.



True, but I still don't think this is a good analogy. When you are learning inheritance you don't know what "x extends y" even means. Using a non-code example obscures what you are talking about.

I'd use an example like this: HPDriver extends PrinterDriver. CanonDriver extends PrinterDriver. EpsonDriver extends PrinterDriver. See, you can make 3 different drivers and share common printer code. That makes sense to me if I'm just learning inheritance. Duck extends animal does not.


I'd object to your example: your example seems to me to be closer to that of instance :: class than subclass :: superclass. PrinterDriver defines a contract; each of HPDriver, CanonDriver etc. will need to fulfill the contract. But I don't see them usefully adding behaviour. The OS would have to already know about HPDriver in order to use its extra functionality - then what's the point in using inheritance at all?


In a lot of OO languages instances rarely have different behaviour to each other.

You are also forgetting that it's possible to have more than one printer using the same driver (i.e. multiple instances of a class of printers). This is not uncommon when you consider network printers.

> The OS would have to already know about HPDriver in order to use its extra functionality

And that isn't true of any other instance in which inheritance is used?


A common pattern is a superclass with "abstract" methods that subclasses then implement. So the `sendCommand(cmd)` function is implemented in HPDriver and CanonDriver and higher level methods in PrinterDriver can use that piece even though PrinterDriver itself has no implementation of that method.


I don't understand how a driver implementation is an instance. I can imagine the HPDriver class having completely different code than the CanonDriver class.

For a realistic version of this example, consider JDBC driver implementations.


Perhaps they are scanner & printer drivers, the implementation exposes a ScannerDriver interface and a PrinterDriver interfaces. They subclass a DeviceDriver that implements the low-level I/O. There are other generic and vendor-specific interfaces that could be used here, as well as perhaps some kind of more specific class on top of DeviceDriver like PCLDriver or PostScriptDriver.

class HPDriver extends DeviceDriver implements ScannerDriver, PrinterDriver


Different printer models could have different features (color/B&W, selecting output tray, “print” an outgoing fax, etc.), and you could make each PrinterDriver subclass responsible for generating the appropriate UI code for frobbing those features.


You can explain is-a using examples that actually make sense to model using inheritance, like a set of game objects that have an interface like:

    class GameObject {
     public:
      // Called every frame to update the object's logical state.
      void Update(GameState* state);

      // Called to draw the object.
      Draw(GraphicsContext* context);
    }

    class EnemySpaceship : public GameObject { /* ... */ };
    class SpaceDebris : public GameObject { /* ... */ };
The point is to move the discussion into the practical reasons for introducing inheritance, rather than to make it a pointless ontological exercise of whether a Square is-a Rectangle or vice-versa.


Most of the benefits of OO programming come from abstracting the machine / concerns (eg. MVC) rather than modeling the domain. (eg. Cars / Ducks / etc).

I know it's just an example but it raises several important questions:

Why does the GameObject need to know how to render itself?

Why does the GameObject update the GameState which the GameObject is ostensibly also a part of?

Does updating the GameState directly affect the ability to separate concerns as to whether the GameState being modified is local vs. remote?

When you start modeling domains it leads very easily to situations where you have hardcoded permissions for the Manager class instead of bothering to implement a permissions system. Or you have a CEO class that's a singleton. (Hopefully, your code doesn't have to work at RIM)

The code savings from

  class DeathStar2 : DeathStar {
    public override FatalFlaw(){}  
  }
is going to pale in comparison to

  class DeathStar2 : Object {
    acts_as_travelling_salesman
    has_many :laser_turrets, :max => 1024
    has_many :tie_fighters, :max => 2048
  }
when applied over many many systems and objects. Modeling domains is to modeling concerns as algebra is to calculus. Most things in life despite their appearances are not hierarchical and thus do not fit well when modelled explicitly using a class hierarchy. Showing people how to model a domain is easy but virtually useless, showing people how to model concerns is hard but is where the big payoffs are.


Modeling a domain right is deceptively hard, and to me is one of the primary skills of our profession. Teaching what OO and inheritance mean is not the same thing as modeling a domain.


The 'CEO singleton' - very funny. :)

However, I'm all for domain modeling in the beginning, for a beginner. I learned OO the hard way - all by myself and pouring over books. I agree with the OP that a vehicle or duck was not a good model. Having worked with databases and crm's in the past (before they were 'crm's), I always dealt with people, organizations, calls, notes, etc. So when I came across an infamous car example, I was always wondering 'this has no bearing on anything I would do - why not talk about real objects like people?'.

Even before hitting any kind of patterns, it's crucial (in my mind) to understand datatypes, methods, scoping, inheritance, etc. 3 Simple classes like Person, Address, Organization can go a long way.

In my mind, there's no way a beginner is going to understand patterns first. They have to be shown the basics and the have to be handheld when entering the OO way of thinking. One those first few steps are taken, then it paves the way for more complex examples and patterns.


Code savings isn't a reasonable goal of OOP. Information-hiding (in which you take an arbitrarily-complicated method and abstract it behind a class) is.


When you give people inheritance all they see is hierarchies and when you give them information hiding systems all they see is access control.

One should not be able to find trade secrets by grepping for "private", nor reproduce a companies org chart by a class inheritance graph.


Do you have an example of modeling concerns vs. modeling domains?


Lets say you're modeling a system with Customers and Salesmen both of which are Persons, should you put that travelling salesman algorithm into the Salesman class or the Person class? That's what I mean by modeling domains.

By modeling concerns I mean that you should create a RouteFinder class and an adapter that extracts the coordinates from Salesman, Persons, and Customers. Maybe there is a convenience method on the Salesman class that makes it easy for him to travel to his customers but most of the work (the concern) is modelled separately from the domain.

The primary concern of the program is routing, not Salesmen and Customers. It's like how rails/ASP.NET MVC/django concerns itself with making websites, not modeling domains. If you focus on the concerns the domain becomes an implementation detail. (eg. For the person class the coordinate can be found at Person.Address.Latitude)


This is really not a good example as games have (read should have) entity systems. See e.g. these two links for an introduction. http://t-machine.org/index.php/2007/09/03/entity-systems-are... http://www.purplepwny.com/blog/?p=215

There should be no Draw() in your game object (read entity) base class. Not every entity is rendered. You should have instead a component Renderable. And if it happens to be so that your special snowflake has a Renderable component then you can render it on screen. E.g. AI pathing nodes would not usually have one unless you are debugging them you can add one.

In your example you end up with Movable extending GameObject, Camera extending GameObject and then you have no way of combining these two behaviours.


Actually, this makes more sense as a trait or interface than as a superclass, especially as you're not even implementing any behavior.

It's the same way for squares and rectangles. Square does the Rectangle and Quadrilateral interface, Rectangle does the Rectangle and Quadrilateral interfaces, and it doesn't matter. If you want a subtype/supertype relationship, then you have to diverge from what people intuitively think about classes. Liskov's Substitution Principle says that subtypes must be completely substitutable anywhere a supertype is used, which means a rectangle isa square because a square does setX, getX, and getY, whereas a rectangle does setX, setY, getX, and getY. (This doesn't make much sense because people use inheritance as "copy and paste this crap from the superclass into my subclass" rather than to setup substitutable subtype/supertype relationships.)

Interfaces avoid that problem because there is no supertype or subtype, only equal types that agree to have the same interfaces. Then it doesn't matter if a square isa rectangle or not.


My nitpick about EnemySpaceship: whether an entity is enemy or not should not be part of its type but part of its state and/or logic.

I know it's "just an example" but to me this thread is about whether arguably incorrect examples are bad practice or not.


Is "Spaceship extends GameObject" really that different than "Duck extends Animal"?


When people say "Duck extends Animal" is bad they typically mean that it's trying to model real-world relationships rather than computational relationships. The example is meant to demonstrate that inheritance should be based on what inheritance means for your program, to make your codebase "nicer", and not to simply categorise your objects.

"Spaceship extends GameObject" exists mostly to facilitate polymorphism and code reuse - virtual function resolution, probably, along with all of the non-virtual data and behaviour associated with every GameObject. A "GameObject" isn't a real thing, it's just a convenience.

Out of context, "Duck extends Animal" could obviously do the same things for the same reasons, but that's not really the point of the example. It's implied that the relationship was used because real ducks are real animals, not because Duck inheriting from Animal is a good idea.


"It's implied that the relationship was used because real ducks are real animals, not because Duck inheriting from Animal is a good idea."

I just do not understand this objection. If you wrote Sim Farm, it's quite possible you'd want Duck to inherit from Bird and Cow and Pig to inherit from Mammal, and both Bird and Mammal to inherit from Animal.

In the course of caring for your farm, you have to fix tractors, grow crops, and care for animals.

All Animals must be fed and will starve if they don't. Why would you code "needs food" on every individual animal class? Birds can get avian flu, and Mammals can get rabies; the pigs can even get rabies from the dogs. Why would you code "can get rabies" on Dog and Pig?

Maybe you don't always want to use Sim Farm as your example for teaching OO, but a lot of working code does model real-world items. Animals and cars and such are no worse than files as objects to model, and have the advantage that non-programmers already know something about them. Hence they can focus on the OO concepts and not on the domain logic.


I'll buy that.

But don't try to convince any OO purists that inheritance "exists mostly to facilitate polymorphism and code reuse". :-)


Yes, because drawing a bunch of game objects on a screen is something a real program might actually want to do, and therefore opens up opportunities to talk about real-world design tradeoffs.

Making a program print "quack" when you call the "speak()" method is something that only happens in textbooks, and since there's no reason to actually do that there's no easy way to discuss the alternatives and why polymorphism is a win.


So what you're saying is that Farmville is the ultimate result of reading OO textbooks, and that if they had spent more time on a realistic model such as spaceships and gameobjects that a game such as Knights of the Old Republic could be produced by Zynga?


That has nothing to do with what haberman is saying. Most people know games, and a simple game model can be expressed in inheritance terms in such a way that's illustrative and less contrived than conventional tutorial metaphors. And I agree with that.


>rather than to make it a pointless ontological exercise of whether a Square is-a Rectangle or vice-versa

it isn't pointless. It is the key point of the design : http://www.objectmentor.com/resources/articles/lsp.pdf


It is pointless if you make it an ontological exercise. If you make it a question of substitutability (as your link does), then I agree that you're doing it the right way.

The question shouldn't be: "is a Square a special kind of Rectangle, in a pure/platonic/logical sense?" The question should be "can a Square be treated as if it were a Rectangle." The answer to the second question could depend on the program and what it wants to do! The first is a rathole that accomplishes nothing.


It is a key point in design AND it usually feels like a completely pointless and annoying waste of time.

Humans have such a marvelous natural facility for attaching a meaning to a sentence that we don't feel like loopholes and contradictions within language are meaningful. Ask the average person "who shaves the barber" in a describing of Russel's paradox and they'll give you a "what was the problem" look rather than any answer at all. see http://en.wikipedia.org/wiki/Barber_paradox

So squares versus circles doesn't seem like a slightly important problem till you have a variation of it starting out of your debugger.

And the other side of this is ... since teasing out these loopholes is really hard and so many exist in potentia, it might really be just as well to leave things confused and correct them as they come up. If you a square class that's not a subclass of a rectangle and your rectangle class has an isSquare method and a toSquare method, well, your code will screwy but you'll have saved thousands of dollars in training fees...


Please do tell of the occasion you found squares versus rects etc. staring at you out of the debugger. Because the solution to this problem depends on the behaviour you're trying to get, rather than some absolute solution, so philosophical argument about it always seemed pointless to me.

If the type is mutable, then per-instance information shouldn't be part of the class; have an isSquare calculated property or whatever. If the types are immutable, then it's OK to embed it in the class hierarchy. If you must have mutable types but you still want to embed it in a polymorphic hierarchy (most usually because of dynamic dispatch reasons, rather than if-casing logic on isSquare), then add a layer of indirection: have SquareBehaviour vs RectBehaviour, as needed. Whatever your solution demands, there's a way of doing it. What the solution doesn't need - nor even cares about - is the philosophical argument.


* Whatever your solution demands, there's a way of doing it. What the solution doesn't need - nor even cares about - is the philosophical argument.*

I think the disagreement here is that you seem to believe that philosophy is engaged in some different activity from the kind of "how should X type relate to Y supertype and Z characteristic" is the meat of object oriented design.

But really, what is being here is ontology. Ontology isn't really fancier than this and the here isn't more clear cut that what philosophers try to muddle out.

Ordinary philosophy has been kind of society-wide clarification of definitions, just a design is an organization-wide clarification of definitions. Take a look at the actual text of The Critique Of Pure Reason at some point. While might have been written with various arguments in mind, most of the actual text is a long, long discussion of what objects belong in what category - ie, nothing more "airy-fairy" than most design discussions.

Ordinary philosophy gets less attention than the elaborate debates around the "edges" of definitions. But this also happens with design discussions.


I agree with what you say, oddly enough. Perhaps my beef is actually with all the amateur philosophers who insist on a unique, canonical ontology, rather than the fact of the matter, that there are always multiple ontologies to choose from. So they argue about things like square and rectangle re subtyping, but there is more than one way to validly slice the pie, so the argument is pointless.


This is in fact more or less the example I suggest using in the article, except that the objects don't have a separate update method.


I agree without caveats.

"Car extends vehicle" and "duck extends bird" are great examples for the type of lesson they're trying to teach. I'm surprised people have time in their lives to worry about this crap.

edit: Also, what's up with the title for this submission?


If it teaches people to think of object-oriented programming as world-modeling, it is not a great lesson. Polymorphism is an abstraction technique whose goal is substitutability. "Car extends Vehicle" is useful if other kinds of Vehicles can be substituted for cars.

If you teach someone that there is value in making Car extend Vehicle just because that expresses a real-world relationship, you are teaching exactly the wrong lesson and your students will create overly complex inheritance hierarchies.


> Polymorphism is an abstraction technique whose goal is substitutability

If you said that to students in lesson #1 of an OOP course no one will understand what you're talking about.

Sure maybe the Duck extends Animal example isn't a realistic one, but you've got to give students a chance to get their heads around the very basics first, and they can at least understand it by using simple real-life things.


>If you teach someone that there is value in making Car extend Vehicle just because that expresses a real-world relationship, you are teaching exactly the wrong lesson and your students will create overly complex inheritance hierarchies.

Only if your students are idiot drones. I think the majority of us went through these same lessons and came out just fine. Stupid is as stupid does (why do I keep saying that so much?) People who are good programmers can take a simplistic lesson like that and grasp the over-all concepts while people destined for terrible careers simply wont. No point bringing down the rest us with them.


Yes, you're right, it's only a great lesson if it's taught properly. I mean, really? Doesn't that seem like hair-splitting to you?

In my opinion, dickering about this is the equivalent of, "Yeah, those two hortizontal parallel lines are a great symbol for equality as far as it goes, but really students need to understand the difference between equivalence and implication!" It's an equals sign.

Same deal here. It's an analogy. The fact that analogies must be taught properly to be useful is a tautology that doesn't even bear mentioning, yet here we are.


I think the point is more that the analogy is so misleading, that it hurts the student's understanding.

A similar bad example would be using familiar round objects to teach equivalence: there are 10 apples, and 10 oranges, so apples == oranges.

Car's don't extend vehicles, and duck's don't extend birds. Just because we can organize these physical things into conceptual hierarchies based on their functionality, doesn't mean we can use them to understand polymorphism.


There is a direct mapping between our natural construction of ontologies (we are pattern-detecting creatures, we naturally see commonalities across the birds) and why object orientation works. Cars are a subset of vehicles in most peoples' minds; and Buttons are a subset of Controls in most UI programmers' minds. And the topologies of these ontologies are similar enough, by design, that you can indeed teach valuable lessons using vehicles and birds.

As to polymorphism, that comes for free from understanding language. Most people will understand a "No vehicles allowed" sign to prohibit cars but not people. What else is that but an understanding of polymorphism in evaluating a predicate?


Object-orientation does not work because cars are a subset of vehicles. Please do not teach anyone that.

Object-orientation works because in some programs, cars can be treated as if they were a vehicle, without knowing what kind of vehicle it is. This is the principle of substitutability, and it's the actual reason that inheritance hierarchies work. But in other programs, cars and other vehicles may not have much in common and should not be part of an inheritance hierarchy!

The real-world ontology is not a reason in and of itself to create an inheritance relationship.


I think you missed my point. The real-world ontology doesn't exist; it's only in our heads. The mechanisms in our heads for these ontologies are what OO uses and why it works for humans.

The point is the commonality of mechanism, not the commonality of any specific ontology. And when you're teaching, that's enough for one lesson.


No, it's not misleading. It is insufficient to capture the totality of what is meant by object-oriented programming and polymorphism. But it is not misleading. It is simply a very narrowly applicable -- but very useful, when leveraged properly -- analogy.


The thing is, people are constantly misunderstanding this point (and attempting to mislead others about it). For example: http://news.ycombinator.com/item?id=2914868


Polymorphism is an abstraction technique whose goal is substitutability. "Car extends Vehicle" is useful if other kinds of Vehicles can be substituted for cars.

"That's great", thinks the student, "I'll keep that in mind on the offchance I ever need to write code for an automatic carwash..."

Honestly I think these examples are fine as far as they go, but they should be immediately followed up with nontrivial examples demonstrating how you might actually use this in programming problems which don't fall into the category "simulation of real-world objects". Otherwise the student can easily dismiss the whole idea as esoteric.


I worry about it because I learned inheritance from using it. I wish I had learned from a teacher. Instead I made hack work-arounds for something that I didn't realize was a common feature of OO languages. Until one day I did some poking and said "oh, THAT'S what extends and implements is for!"


His point is that you can't add code to ducks... full stop. If you want to talk about "software [that] tracks ducks", then do so, but by talking about physical ducks one further encourages the ancient fallacy that object orientation is about physical modeling and should be all about physical objects and their physical relationships. What was said was what was meant; you can not add code to ducks. They shouldn't be in the tutorial at all, just as Chevrolets shouldn't be.


You can't add code to ducks any more than you can add code to sockets. Let's assume for a moment the hypothetical student in this scenario is bright enough to tell the difference between literally describing a thing, and expressing an abstraction of it.

So basically then, the thing we're all supposedly getting in a kerfluffle about is that the example program in a hypothetical textbook that introduces the concept of inheritance is a cheap duck simulator. Ducks are a corny example, so I think it's fair to dismiss it as bland. But talking about physical objects is a great way to introduce inheritance to a novice because inheritance is fundamentally about hierarchical abstraction (code simplicity and reuse, incidentally, are side benefits enjoyed by all methods of abstraction). And what easier way to talk about hierarchical abstraction than the most accessible mental model already possessed by anyone who made it out of elementary school – the animal kingdom. The easiest pedagogical metaphors to grasp for students without special knowledge are those that draw similarities to what they already know.

OO is about a lot of other things too. But concepts such as Dependency Injection, which was the meat of the grandfather rant, are IMHO more an outgrowth of dealing with the limitations of OO design than a topic so fundamental to objects and classes that they need to be discussed when you're still talking about fundamentals like these. Are they good things to know about? Definitely. Will a student's mind be warped by basic inheritance example that imitates a taxonomy he already understands? As long as the whole book isn't about duck modeling, probably not, and even then I'm not sure you couldn't teach <IQuackable> FactoryFactories with a little imagination.


If someone is actually writing about a bird-flock simulator or something, I think Duck is a perfectly fine example. My beef is with Duck being presented in the abstract, disembodied.

> concepts such as Dependency Injection, which was the meat of the grandfather rant, are IMHO more an outgrowth of dealing with the limitations of OO design...

I don't think so. The inflexibility you can loosen with DI exists in all kinds of non-OO and even non-imperative programs as well. In a sense, the whole point of object-orientation is that it gives you a handle on that kind of thing: the thing you depend on is an object reified at run-time, which you can arrange to have passed in to you instead of extracted from a global namespace, and which can be replaced with some other object with different behavior, not an address you are jumping to that's hard-coded into your compiled jump instruction as an immediate argument.


Interesting. I suppose when I think of DI I usually think of Java. Do you know of a good example of the DI pattern being used in functional languages?


map and reduce. DI is so prevalent in functional programming that it's pretty much taken for granted.


Well, take this Clojure tutorial, for instance, comparing a simple DI implementation in Java with a single function in Clojure. The latter bears almost no resemblance to the former, other than achieving the desired effect. Is it fair to say that the user of a closure is implementing the Dependency Injection pattern, or is it a positive side effect of the fundamental properties of first class functions and lexical scope?

http://vimeo.com/10368175


IMHO it's largely a side effect of first class functions. In Java in order to pass a function you need to pass an object that has the function, or an interface with the function, so it leads to a lot of line noise and thus people don't do it much. DI doesn't look impressive in a functional language because it's so well supported, but if you look at something like a global accumulator in a functional language you'll need a monad because you need state.

eg in Java it's easier to write:

  int accum = 0;
  for(int i : collection){
    accum +=i;
  }
rather than

  collection.reduce(new IReduce<int> { 
    int reduce(int accum, int i){
      return i + accum;
    }
    })
I'm a bit rusty on Java so the syntax may be off but you get the idea, where as in a functional language (F#) that code is reduced to:

  collection 
  |> Seq.reduce +
Or in an imperative language that supports function passing (C#)

  collection.sum((a,b) => a+b)


>you can not add code to ducks //

[I'm in a bit over my head but] Sure you can. This is just the sort of thing that happens in such tutorials - "now lets make all our ducks say moo if they're speaking but on land at the time".

When I learnt OO we used frogs, in SmallTalk, as our analogy. I don't think I ever once thought that the "frog" was in some way limited to things frogs could do, only that it was representing a series of things with a particular relationship wherein those things could have different characteristics and behaviours.


So domain modeling has no place in software? The burden of proof for such a claim is a bit higher than "you can't add code to ducks".

That ducks are physical is irrelevant in any case. You can't add code to bank accounts or insurance policies or leap years either.


"You can't add code to payrolls or insurance policies or bank accounts either."

No... you can't. Exactly. OO isn't about real-world-objects, it's still about code-objects, and mixing the two up causes serious category errors.

Physical modeling is the wrong way to do it, it's the wrong way to teach it, it's the wrong way to conceptualize it, it should not be used in tutorials.

Domain modeling != physical modeling, and conflating the two is exactly the problem being addressed. You know you're doing physical modeling when you have a need for an iterator class, but you don't think you can use it, because what on earth is an "iterator"? You can't hold one of those in your hand.

The map is not the territory!


I don't get what you're saying. What does it matter whether software is modeling a physical system or, say, a business one? Either way you're drawing on concepts from some domain that exists prior to the software you're building. Either way you're trying to find representations of those concepts suitable for computing what you need to compute. The art of domain modeling is making those representations intelligible in domain terms. This has nothing to do with physicality. Of Evans' canonical examples in Domain Driven Design, one has to do with paint-mixing and another with accounting. The techniques are no different.


"The art of domain modeling is making those representations intelligible in domain terms. This has nothing to do with physicality."

Yes, exactly. I do love how often people cite back my own points at me as if they are disagreeing.

I think perhaps people have forgotten the origins of OO. A lot of people were taught that the right way to do OO is to match the physical model of the domain they are trying to program for. It's great that you and so many other people have either thoroughly internalized how untrue that is, or were never taught that model in the first place. But I for one was, as were a great deal of the rest of my generation who learned about OO in the 90s, and it's actually somewhat important that we finish stomping the idea out that physical mapping is at all an important aspect of OO.

And the best place to stomp it out of is in the Standard OO Tutorial (TM).

If you and all the excited downmodders never learned that bad idea in the first place, great! Count yourselves amongst the lucky people who probably also never had to be broken of things like line numbering, or two-letter variable names. (If you think I'm joking, I only wish I could show you some of the first-C++-class homework I graded back in 2000. I'm not joking.) In the meantime, this is a real thing that is still floating around in real curricula today, and you may join me in boggling at this fact, but it doesn't change its truth.

It actually stuns me that some of employees that we've hired who graduated as recently as last year appear to have received the exact same OO education that I did in 1999, which was already pretty creaky then.

The map is not the territory.


"I do love how often people cite back my own points at me as if they are disagreeing."

For what it's worth, when I read that I found it unpleasant in more ways than one and it killed my interest in further discussion. Perhaps that was for the best, as I didn't seem to be making any progress in understanding you.


A lot of OO was, in fact, influenced by the task of writing programs for simulations. It's conceivable someone might actually want to develop animal/bird/duck classes (e.g., SimAnimals). http://en.wikipedia.org/wiki/Simula


Yes, I know. That's where the idea came from. Nevertheless, it's a terrible introduction to OO. It promotes very wrong conceptualizations and horrible code.

And odds are, even if that is the type of code you're writing, you still shouldn't be trying to have a one-to-one mapping between physical things and classes and/or instances. It just isn't a very good way to work.

Just because it was the first thing that was done with OO doesn't mean that it was actually a good idea. (Actually, the whole idea that the first person to implement a technology or methodology is forever the one and only true authority the one and only true definition is a bizarre one anyhow; of all the people who implement a particular methodology, isn't a bit much to expect the very first person to get every detail correct? You can see this in a lot of purist debates in our discipline.)


There are lots of ways to structure programs, and talk about how programs are structured. Some are good for some programs, others are good for others.

Knowing when to use which metamodel is an art that is only learned after long experience.

A lot of this discussion about the inherent superiority of spaceships to ducks seems to reflect the familiarity bias of the participants as much as anything else. There are certainly interesting points being made here, but maybe game experience isn't as ubiquitous as some seem to think.

On the other hand, maybe game developers could learn a thing or two about ducks. In Minecraft, for instance, they cluck like chickens.


Simula67, the version most identify as Simula proper, actually evolved concurrently with Smalltalk.

Rick DeNatale's memoir has a great article debunking some early OO myths -- http://talklikeaduck.denhaven2.com/2006/07/29/about-me


Personally I'd add the code to "TrackedObject" rather than "Duck" just in case we ever wanted to track geese or cormorants.

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.

Perhaps, though I think his real point is that often folks teaching software development will only put out that first hurdle and forget about the rest. Teach 'em that "ducks are animals" and think they understand inheritance, when in fact these simple physical analogies are a bad way of understanding how and why you might use inheritance in the real world.

I think it's a phenomenon quite common across the whole OO pedagogy, in fact. If your only mental examples of "objects" are ducks and bicycles then you're going to get pretty confused when you escape simple example land and start trying to understand the difference between an NSView and an NSViewController.


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. 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.


> 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.


The 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.)




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: