Hacker News new | past | comments | ask | show | jobs | submit login

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.




Join us for AI Startup School this June 16-17 in San Francisco!

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

Search: