views:

3552

answers:

14

I'm writing documentation for an object-oriented language, and I wonder what kind of classes would be a good example for inheritance.

Some common examples:

class Person {
}
class Employee extends Person {
}

Currently my favorite, but I don't like Person->Employee because 'Employee' does not exactly look like fun.

class Bicycle {
}
class MountainBike extends Bicycle {
}

I found this in some Java tutorial, but it's not really obvious what attributes a bike should have.

class Animal {
}
class Bird extends Animal {
}

Same as the bicycle.

class A {
}
class B extends A {
}

Too abstract. The main problem is that such a class will need even more abstract attributes and methods.

Does anyone have a better example for a simple class hierarchy?

+5  A: 

I like the Stream hierarchy. The idea is that anything can use a stream without usually caring what kind of stream it is, and individual subclasses handle the storage differently (e.g. NetworkStream, MemoryStream and FileStream in .NET).

If you're interested in interfaces, then IEnumerable<T> in .NET is a great one - you can iterate over any collection without caring what the underlying data structure is.

Jon Skeet
I have also thought about this (or rather Java's InputStream), but I think Streams are not something that everyone will understand. Especially for my target group, since I am writing a reference for JavaFX.
Tim Jansen
If someone is writing JavaFX they *should* understand core ideas like streams, IMO.
Jon Skeet
I am not sure, but I'd guess a large part of the audience are people who come from a design background, rather than a technical background.
Tim Jansen
+2  A: 

I always was fond of:

class Shape {
}
class Square extends Shape {
}

But any of the top three you quote would be fine. MountainBike sounds the most exciting. You can do similar things with cars of course.

Iain M Norman
The square-example is problematic when rectangles come into play. The relation between rectangles and squares is a bit in counter-intuitive.
Rik
The classes Shape and Square are easy to understand, but what would be the attributes of a Shape? I couldn't think of any, only abstract methods. And abstract methods are something that I would like to introduce after inheritance, not in the same section.
Tim Jansen
"Shape" is my favorite illustration of object orientation.
mizipzor
A: 

The best one I have always used has been Shapes.

You can then have

Shape --> Quadrilateral
Quadrilateral --> Rectangle
Quadrilateral --> Trapezoid
Rectangle --> Square
Shape --> Triangle

etc.

Methods are then also easy to guess at.

Shape::Area
Shape::Draw
Shape::Intersect (x,y)
Hortitude
Square is not a substitute of a rectangle, see one of the answers above.
quant_dev
While I understand the above comment I would still stick by this example as being perhaps the easiest to explain the principles of OO to new people. You can just look at how many people used this example here to understand its power.
Hortitude
You're not explaining but confusing.
quant_dev
+1  A: 

I think Shape is a good abstract class. There are both 2D and 3D shapes. The 2D shapes typically have area while 3D shapes have volume. Both can have a "location" or "mass center".

Some suggestions:

class Shape {..}

class Shape2D extends Shape {...}

class Circle extends Shape2D {...}

class Rectangle extends Shape2D {...}

class Polygon extends Shape2D {...}

class Shape3D extends Shape {...}

class Sphere extends Shape3D {...}
norheim.se
Actually, this is a pretty good shape example. Just avoid the square shape.
Casebash
+2  A: 

Auto Parts can be interesting for example you might have

class part
{
    OEM
    Manufacturer
    Number
    Description
}

class Tire extends Part
{
   Speed
   Rating

}
JoshBerke
Wow, finally an example which could really occur, wouldn't better be written as an interface and isn't abstract.
Casebash
Thanks, I spent almost 10 years working one code in the automotive industry. Lots of great examples when you deal with parts especially if your managing many manufacturers, aftermarket, refurbished....It's a complete mess
JoshBerke
A: 

I like the vehicles example, as it allows for a relatively clean extension to include interfaces into the discussion (IAutomaticGearbox, anyone?)

Rowland Shaw
A: 

What about

class Weapon 
{
}

class Gun : extends Weapon
{
}

class Knife : extends Weapon
{
}

et.

Muad'Dib
+3  A: 

The Animal class is the classic example of class inheritance for a number of reasons.

First, there are obvious ways to extend the underlying animal class. You'll likely start with sub-classes such as Mammal, Bird, Crustacean, etc.

Some classes, such as Mammal, will extend Animal by adding attributes that are fairly obvious ("Warm-Blooded", etc.).

Other, more problematic, issues that are quite common in developing a class hierarchy are quite obvious if you illustrate with animal inheritance - and this is a good thing for purposes of explanation. Birds fly, right? Well, not all birds... So, how do you represent flying? There are, of course, classic solutions and a wealth of discussion information online about how to solve the problems and tradeoffs that each solution introduces.

Thus, I would highly recommend using "Animal" as your example because of the richness of available information and examples.

Mark Brittingham
And also the classic Animal.Move() ...
chakrit
It also illustrates the "because we can" tendency to fit *everything* under the sun into the same big bloated hierarchy - a "feature" of inheritance that I'm not sure I'd want to highlight as a good example. It's lacking the pretty fundamental "but why would we do this", that other examples, like the Streams one, has. Does our application really need penguins and ostriches to share a base class?
jalf
Ummm..jalf, I think the point is to demonstrate to newbies the foundations of Object Hierarchies in terms that are familiar to them. If you start with streams, you'll find that many people have no conceptual framework on which to build and thus you'll simply lose them. It may well make sense to teach them the finer points once they get the hang of the basics. Of course, if you simply enjoy the thought of screwing newbies because programming *should* be hard and, anyway, it proves how smart you are to demand hard examples, then you might want to consider downvoting my answer. Oh wait...
Mark Brittingham
What kind of programming application would you model such a hierarchy? A simulation, a database of animal facts, a farm tracking system?
Casebash
A: 

Inheritance can be complex. Let's start with the simplest of 'em all -- behavior inheritance. Here you are inheriting behavior only and no state. E.g: Both Person and Animal are Animate objects which exhibit an IsAlive behavior. To use your example:

class LivingThing {
   /* We propose a new type */
  public:
    virtual bool IsAlive() = 0;
    virtual void Birth() = 0;
    virtual void Death() = 0;
};

class Person : public  LivingThing {
    /* A real living thing */
  public:
    virtual bool IsAlive() { return true; }
    virtual void Birth() {}
    virtual void Death() {}
    /* .. and has some special behavior */
    void Marry();
    void Divorce();
};

class Animal: public  LivingThing {
    /* A real living thing */
  public:
    virtual bool IsAlive() { return true; }
    virtual void Birth() {}
    virtual void Death() {}
    /* .. and has some special behavior */
    void Bite();
    void Bark();
};

I wrote this using C++ syntax, if you have problems understanding any of it, just say so!

dirkgently
A: 

The best example that i have came across (and read in many books) is the one that uses Shape.

The best thing about this is that you can very easily explain all the concepts (including the tough ones ) related to OOPs like Class,Object,Inheritance,Abstraction,Encapsulation,Polymorphism,etc to any programmer irrelevant of his experience.

A: 

Example:

The "Everything derives from Object" approach.

Dog --> Animal --> Living thing --> Object

A Dog is an Animal, which is a Living thing, which in turn is an Object.

Elroy
+2  A: 

Many people use the Shapes example, but that is in fact a dangerous one. The problem arises when you intuitively decide that a square is a subclass of rectangle.

When it comes to behavior, a square is more limited than a rectangle, breaking substitutability. For example, we could ask a rectangle object to change its height. If a square is a subclass of rectangle, that means we should be able to ask the same of a square. However, changing the height of a square would mean its not a square anymore! Of course, we could increase the width accordingly, but that's not what we would expect when we were to ask an object of declared type rectangle, which is actually a square underneath, to change its height.

It's called the Liskov substitution principle, and you should be aware of it when doing any serious OO development.

Squares are, of course a sub*set* of rectangles, instead of a sub*class*. This is the difference between data-oriented and behaviour-oriented approaches.

Like Jon, I prefer Streams as an example. It's not difficult to explain, even to non-programmers, and its cleary behaviour-oriented, avoiding the counter-intuitivity of the shapes-example.

Rik
Can you give an example of when subclassing square from rectangle is a problem? I would point out that since a square is a true subset anytime you can use a rectangle you can use a square. The reverse is not true, but many many subclasses add functionality and constraints (as they should)
Hortitude
I've added an example. Remember we are concerned with substitutability of _behaviour_ instead of _data_. When you're talking about data, you're right; any method that can deal with some class of data should be able to handle any subclass of it. But, that's not what OO is about.
Rik
Thanks for the example. Makes sense.
Hortitude
+2  A: 

I suggest 'devices'. Nobody really models animals using software, but they do model devices.

class Device
{
  void start();
  void stop();
  DeviceStatus status { get; }
}

class VideoDevice : Device
{
  ... methods for any/all video devices ...
}

class DiskDevice : Device
{
  ... methods for any/all disk devices ...
}
ChrisW
Devices would generally be modeled using an interface
Casebash
+2  A: 

I agree with Jon Skeet on his streams example. Perhaps it's not perfect, but it has one advantage over most of of the examples here:

It is realistic

Bicycles, persons or animals, shapes or weapons just wouldn't be modelled by inheritance in real projects. (shapes in particular, are downright dangerous, because it doesn't work.)

That's my pet peeve with inheritance. It is too often taught as something that must be used to express every hierarchy you can find. An employee is a person, right? So the Employee class must inherit from a Person class. But a person is also a LivingCreature, so we'd better have one of those classes too. And a LivingCreature is also an Organism, so there we have another class. And an Organism is.... feel free to continue.

I think it'd be nice if someone, somewhere, actually taught inheritance by explaining when it should be used, and not just how you can force it down over any hierarchy, whether it's beneficial or not.

Streams (or devices as in ChrisW's example) have the advantage that they make sense. You want to be able to treat all streams the same, whether they're connected to a memory buffer, a file or a network socket. And all hardware devices do have a lot of behavior in common that could plausibly be factored out into a Device base class.

jalf
I would +1000 this if I could, because I completely agree with you. I've always loathed the textbook examples of inheritance for exactly the reasons you mention here. Real, useful, and good examples of inheritance are few and far between, and CS classes tend to focus on inheritance without explaining how to use it appropriately.
Mike Spross
If I sound a little excited, it's because I'm looking at a programming languages course textbook, and I just got to the Person class example (ugh). "True-to-life" inheritance sounds nice, but just doesn't work out very well in practice. Abstract concepts, like streams, seem to be better-suited as candidates for inheritance, because you don't have nearly as much ambiguity in terms. A FileStream is a Stream is an Object. It would take effort to dream up additional Stream superclasses in that hierarchy. Real-life objects tend to lead you down a rabbit hole of never-ending inheritance...
Mike Spross