34

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?

Tim Jansen
  • 3,330
  • 2
  • 23
  • 28
  • Here is an implementation of shapes in java. [Shapes implementation in Java](https://github.com/rajeevs1992/myCodes/blob/master/java/shapes/Shape.class "Shapes:Full code") – rjv Jul 22 '12 at 12:14
  • 2
    All these years and no one can give a legitimate business example. OOP is the dumbest thing I have ever seen. It was invented by people who do not have any problems to solve, or work for a living. –  Jun 30 '15 at 17:30

22 Answers22

27

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
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 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 Feb 22 '09 at 17:03
  • If someone is writing JavaFX they *should* understand core ideas like streams, IMO. – Jon Skeet Feb 22 '09 at 17:11
  • 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 Feb 23 '09 at 00:04
  • 3
    Isn't stream example of interface, not inheritance? – MK. Feb 11 '11 at 19:47
  • @MK: No, because Stream isn't an interface - it's an abstract class. – Jon Skeet Feb 11 '11 at 19:48
  • @Jon Skeet yes, it is, but is there any sharing of state or methods of substance between them? The purpose of extending Stream is to adhere to an interface contract, not to reuse some code from the parent. – MK. Feb 11 '11 at 19:55
  • @MK: How do you explain the presence of non-abstract methods in Stream then? Why do you think Stream *is* an abstract class instead of an interface? – Jon Skeet Feb 11 '11 at 19:58
  • @Jon Skeet I think it is abstract. Both InputStream in Java and System.IO.Stream are abstract. Even Bill O'Reilly can't explain that! – MK. Feb 11 '11 at 20:05
  • @MK: But I can... because there are methods in `Stream` which have implementations, so don't have to be overridden in individual subclasses. That's my point. – Jon Skeet Feb 11 '11 at 20:08
  • @Jon Skeet ah, ok I see your point. – MK. Feb 11 '11 at 20:12
  • I tend to agree with all of you. It's an illustrative example of sharing code (type inheritance) when you develop your own stream, but it's also a good example of having a common contract (interface implementation) when you write something that _uses_ a stream. However I would say the concept of streams could also be implemented with an `IStream` interface and a bunch of extension methods (`CopyTo` and friends). – Johannes Egger Mar 11 '19 at 16:45
22

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
  • 243,077
  • 51
  • 345
  • 550
  • 2
    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 May 07 '09 at 04:57
  • 1
    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 May 07 '09 at 05:21
19

Auto Parts can be interesting for example you might have

class part
{
    OEM
    Manufacturer
    Number
    Description
}

class Tire extends Part
{
   Speed
   Rating

}
JoshBerke
  • 66,142
  • 25
  • 126
  • 164
  • 1
    Wow, finally an example which could really occur, wouldn't better be written as an interface and isn't abstract. – Casebash Jan 23 '10 at 05:04
  • 1
    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 Feb 01 '10 at 14:44
  • 1
    I was writing an article about jpa inheritance hence was thinking of some real world example and was thinking about the vehicular hierarchy. Your answer made me stick to my decision :) – Prasad Kharkar May 07 '14 at 16:24
  • 1
    I like this one because it makes sense without virtual functions, unlike most other suggestions. It does lack behavior though. – Alex Mar 25 '20 at 17:44
12

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
  • 28,545
  • 12
  • 80
  • 110
  • And also the classic Animal.Move() ... – chakrit Feb 22 '09 at 17:14
  • 13
    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 May 07 '09 at 13:28
  • 3
    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 May 08 '09 at 01:52
  • 6
    What kind of programming application would you model such a hierarchy? A simulation, a database of animal facts, a farm tracking system? – Casebash Jan 23 '10 at 04:53
  • 5
    then don't start with streams. But start with something *meaningful*, something where an inheritance relationship actually might be *useful*. And if you can't think of such a case, then perhaps everyone would be better off if you didn't try to teach inheritance *at all*. If you want to talk about "screwing newbies", then by all means tell them that an application *must* give penguins, ostriches, cats and dolphins a common base class. That's real helpful to someone trying to learn programming. – jalf Jun 26 '11 at 18:32
11

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 subset of rectangles, instead of a subclass. 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
  • 28,507
  • 14
  • 48
  • 67
  • 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 Feb 22 '09 at 17:23
  • 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 Feb 22 '09 at 17:37
  • 5
    Just to clarify the Square vs. Rectangle example, this is only a problem if you have setter methods like setWidth(),setHeight(). Then the square can either a) break it's invariant (height=width) b) add behaviour to preserve the invariant (i.e. set two variables), both of which are undesirable. If the Rectangle and Square are immutable objects then you won't break LSP. – Pall Melsted Nov 03 '11 at 12:01
9

If you are into video games could be something like this:

class enemy{
  Health
  Posx
  posy
  Etc
}

class orc : extends enemy{
 speed
 Strength
 etc
}
pb2q
  • 58,613
  • 19
  • 146
  • 147
luis
  • 91
  • 1
  • 1
9

What about a hierarchy of algebraic expressions. It is an excellent example because it uses both inheritance and composition:

public interface Expression {
    int evaluate();

    public class Constant implements Expression {

        private final int value;

        public Constant(int value) {
            this.value = value;
        }

        @Override
        public int evaluate() {
            return this.value;
        }

        @Override
        public String toString() {
            return String.format(" %d ", this.value);
        }

    }

    public class Negate implements Expression {

        private final Expression expression;

        public Negate(Expression expression) {
            this.expression = expression;
        }

        @Override
        public int evaluate() {
            return -(this.expression.evaluate());
        }

        @Override
        public String toString() {
            return String.format(" -%s ", this.expression);
        }
    }

    public class Exponent implements Expression {

        private final Expression expression;
        private final int exponent;

        public Exponent(Expression expression, int exponent) {
            this.expression = expression;
            this.exponent = exponent;
        }

        @Override
        public int evaluate() {
            return (int) Math.pow(this.expression.evaluate(), this.exponent);
        }

        @Override
        public String toString() {
            return String.format(" %s ^ %d", this.expression, this.exponent);
        }

    }

    public class Addition implements Expression {

        private final Expression left;
        private final Expression right;

        public Addition(Expression left, Expression right) {
            this.left = left;
            this.right = right;
        }

        @Override
        public int evaluate() {
            return this.left.evaluate() + this.right.evaluate();
        }

        @Override
        public String toString() {
            return String.format(" (%s + %s) ", this.left, this.right);
        }
    }

    public class Multiplication implements Expression {

        private final Expression left;
        private final Expression right;

        public Multiplication(Expression left, Expression right) {
            this.left = left;
            this.right = right;
        }

        @Override
        public int evaluate() {
            return this.left.evaluate() *  this.right.evaluate();
        }

        @Override
        public String toString() {
            return String.format(" (%s * %s) ", this.left, this.right);
        }
    }

}

Then you can provide a motivating example like:

public static void main(String[] args) {

    Expression two = new Constant(2);
    Expression four = new Constant(4);
    Expression negOne = new Negate(new Constant(1));
    Expression sumTwoFour = new Addition(two, four);
    Expression mult = new Multiplication(sumTwoFour, negOne);
    Expression exp = new Exponent(mult, 2);
    Expression res = new Addition(exp, new Constant(1));

    System.out.println(res + " = " + res.evaluate());

}

Which would yield:

(  ( ( 2  +  4 )  *  - 1  )  ^ 2 +  1 )  = 37
Edwin Dalorzo
  • 76,803
  • 25
  • 144
  • 205
6

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 {...}
SteinNorheim
  • 2,197
  • 1
  • 15
  • 21
4

Being a counter-strike game admirer this is what I would love to share:

enter image description here

Yasser Shaikh
  • 46,934
  • 46
  • 204
  • 281
  • 1
    I like games. Warcraft might be good: types of soldiers, types of moving objects, types of objects you can destroy... – curiousguy Sep 26 '17 at 21:58
4

I use to show Chess pieces. The base class is the general ChessPiece, King, Rook, Bishop, etc inherits.

What I like about this example:

  • it is real, if you implement chess, this is a solution
  • in C++ you can show which function is virtual and which is not
  • abstract class can be shown
  • advanced example may develop towards common base class for directional pieces (Rook and Bishop) or even further to generalized directional pieces with step limit (everything but Pawn)
4

Webshop could be a good option as well.

Product(id, title, price, description)
Book(isbn, publisher, pageNr) extends Product
CD(company, duration, genre, taskList) extends Product
Victor
  • 325
  • 4
  • 12
  • 3
    This is the example I found to be the best. It's not abstact like animals (abstract classes as 1st inheritance lesson would be an overkill), it's not a problematic hierarchy of shapes and it's a real-life example that can be easily understood. – Xeverous Jul 16 '18 at 12:50
3

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
  • 54,973
  • 13
  • 116
  • 224
2

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
  • 2,075
  • 15
  • 30
  • 1
    The square-example is problematic when rectangles come into play. The relation between rectangles and squares is a bit in counter-intuitive. – Rik Feb 22 '09 at 17:04
  • 1
    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 Feb 22 '09 at 17:05
  • "Shape" is my favorite illustration of object orientation. – Mizipzor Feb 26 '09 at 15:09
  • 1
    The problem with using concepts as examples is: what do you program? What would a shape really DO in a business context? The problem with using real objects (animals etc) as examples is... they don't need any programming, animals already DO whatever it is that they do. So, we need examples of actual situations where it makes sense to write a program to accomplish a legitimate objective where a computer is the best choice, using hierarchy. I think that there are very few commonly understandable examples of that. Streams, devices, etc are not the first thing most people can learn about. –  Jun 30 '15 at 17:26
1

I like the idea of using Printers as an example. Say you're working for HP, Brother, Lexmark, or some other entity, and you need to come up with a program that is capable of generating driver specific modules for a variety of operating systems.

class Printer {
    private String brand;
    private String model;

    public void powerOn() {
        // Logic to power on
    }

    public void powerOff() {
        // Logic to power off
    }

    public void init() {
        // Bootstrap/prep the system
        // Check cartridge
        // Check paper
        // Ready for usage
}

class InkJetPrinter extends Printer {
    // Inherits fields and methods from Printer
    // Demonstrate method overriding and overloading
    // Add new InkJet specific behaviors, components etc.
}

class LaserPrinter extends Printer {
    // Inherits fields and methods from Printer
    // Demonstrate method overriding and overloading
    // Add new Laser specific behaviors, components etc.
}

class LabelPrinter extends Printer {
    // Inherits fields and methods from Printer
    // Demonstrate method overriding and overloading
    // Add new Label specific behaviors, components etc.
}

After demonstrating inheritance you can move towards abstraction and method overload/override. The init method in Printer class is a good candidate for abstraction. Subclasses will need to implement their own initialization processes.

You can also extend this example and start demonstrating proper usage of Interfaces and Composition. What is the difference between a IS-A and HAS-A concept? Some printers may have WiFi and Bluetooth capabilities. Or, Some of the InkJet printers may have scanning capabilities while other only have feeders etc. How would you implement this?

I think using printers is more closely related to computers and computer science in general. You can use this example and even further demonstrate examples that deal with networking between embedded systems and PC's, SmartPhones, and other IoT's.

Emir Memic
  • 270
  • 1
  • 3
  • 14
1

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.

Santosh Gokak
  • 3,393
  • 3
  • 22
  • 24
0

You can find good examples of class inheritance in design-patterns.

  1. Abstract_factory_pattern : Provides a way to encapsulate a group of individual factories that have a common theme without specifying their concrete class

  2. Template_method_pattern: It is a behavioral design pattern that defines the program skeleton of an algorithm in an operation, deferring some steps to subclasses.

  3. Decorator_pattern: It is a design pattern that allows behavior to be added to an individual object, either statically or dynamically, without affecting the behavior of other objects from the same class

Refer to below posts for real world examples:

When to Use the Decorator Pattern?

Template design pattern in JDK, could not find a method defining set of methods to be executed in order

Ravindra babu
  • 37,698
  • 11
  • 250
  • 211
0

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
  • 13,638
  • 16
  • 58
  • 72
0

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

Rowland Shaw
  • 37,700
  • 14
  • 97
  • 166
0

What about

class Weapon 
{
}

class Gun : extends Weapon
{
}

class Knife : extends Weapon
{
}

et.

Muad'Dib
  • 28,542
  • 5
  • 55
  • 68
0

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
  • 108,024
  • 16
  • 131
  • 187
0

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
  • 605
  • 4
  • 12
  • 20
-2

Hierarchy of Shapes

I completely understand the concerns regarding the relationship between Rectangles and Squares regarding Liskov's substitution principle. However, there are ways to preserve this principle and still have Square as subclass of Rectangle. For example (Python), assume the following:

class Rectangle:
  def __init__(self, w, h):
     self.width = w
     self.height = h

  def setWidth(self, neww):
     if neww > 0: self.width = neww

  def setHeight(self, newh):
     if newh > 0: self.height = newh

We can define Square the following way:

class Square(Rectangle):
   def __init__(self, side):
       super().__init__(self, side, side) # width = height = side

   def setWidth(self, newside):
       super().setWidth(self, newside)
       super().setHeight(self, newside) # force height to match

   def setHeight(self, newside):
       super().setWidth(self, newside)  # force width to match
       super().setHeight(self, newside)

This way we can always use an Square instance whenever a Rectangle instance is needed, thus preserving Liskov's.

Now, from my experience, we can also use Inheritance to enforce a policy (usually restrictive). For example, assume we have a Shape class with a color property and corresponding setter (setColor).

class Shape:

   def __init__(self, ..., color, ...):
        # ...
        self.color = color
        # ...

   def setColor(self, newcolor):
        self.color = newcolor

   #...

Assume we want to create a "RedShape" class. How can we prevent client code from changing the color? Answer: de-active the setColor method by overriding it.

class RedShape(Shape):
   def __init__(self, ...):
      super().__init__(self, 'red', ...)


   def setColor(self, newcolor):
      pass

We redefined setColor in RedShape to do nothing.

The setColor in RedShape overrides the one in Shape so client code cannot change the color of RedShape instances. In a way, we created a more restrictive subclass because we do not have the ability of changing its color, but it does not violate the substitution principle.

This model is useful when we have base classes that are generic (not abstract) and we create specialized classes to control or limit the features available in the base class. You can create a chain of subclasses increasingly enabling features available in the base class, or you can create classes that offer different combinations of features available in the base class.

Before I get flamed, let me reiterate that this usage is a pragmatic model that takes advantage of the mechanism of inheritance and polymorphism for control. It is far removed from the romantic notion of inheritance of creating new classes by enhancing or refining existing ones.