Monday, November 26, 2012

Quick Tip: The OOP Principle of Inheritance

This entry is part 6 of 6 in the series Beginner's Guide to OOP

We’ve come a long way in this beginner’s guide to object-oriented programming, discussing the principles of cohesion, coupling, encapsulation, and abstraction. In this final article, we’ll discuss the OOP principle of inheritance and its uses in game development. Note: Although this tutorial is written using Java, you should be able to use the same techniques and concepts in almost any game development environment.

What Is Inheritance?

Inheritance is the principle of class hierarchy. It is the ability for one object to take on the states, behaviors, and functionality of another object. A real-world example of inheritance is genetic inheritance. We all receive genes from both our parents that then define who we are. We share qualities of both our parents, and yet at the same time are different from them. Objects in OOP can do the same thing. Parent classes can have child classes (also known as superclasses and subclasses respectively) which can have the same properties of the parent class, and can define new states, behaviors, and functionality of their own. As an example, consider the following class that could be used as a parent class to different shapes:
public class Shape {
  protected int height;
  protected int width;

  public Shape(int h, int w) {
    height = h;
    width = w;
  }

  public int area() {
    return height * width;
  }

  public int getHeight() { return height; }
  public int getWidth() { return width; }
  public void setHeight(int h) { return height; }
  public void setWidth(int w) { return width; }
}
To extend this class to implement a triangle, it would look like this:
public class Triangle extends Shape {
  public Triangle(int h, int w) {
    super(h, w);
  }

  public int area() {
    return super.area() / 2;
  }
}
Triangle has all the same states and functions as Shape, but redefines the area() function to return the proper area of a Triangle (half base times height). The keyword super is used to reference the superclass and any of its states and functions. This is why we can use super() to call the constructor of the superclass and super.area() to call the area() function of the superclass. So, in this case, super.area() returns height * width. The protected keyword is the last access level modifier. It acts like the private access level modifier but also allows any subclasses to have access to the variable or function.

Why Is It Helpful?

As you can see, inheritance can greatly help reduce code redundancy between similar objects by taking what those objects have in common and putting them in one place. This also creates more maintainable code because it helps to comply with the principle of DRY and to prevent the ripple effect in code changes. If all of this seems familiar, it’s probably because abstraction had very similar benefits (as well as most of the other principles of OOP). Abstraction is closely related to inheritance as an abstracted class can be used as a superclass to create subclasses. The only difference between an abstract class and a normal class is that an abstract class cannot be used to create an object.

How to Apply It

Lets go back to our three games one more time to describe how to apply inheritance.

Asteroids

Recall that we defined an abstract class for moving objects across a screen. Also recall that we defined a Ship class for the ship object. To apply inheritance to Asteroids, we can have the Ship class extend the Movable class as follows:
/**
 * The Ship Class
 */
public class Ship extends Movable {
  /**
   * Function to rotate the ship
   */
  public void rotate() {
    // Code that turns the ship
  }

  /**
   * Function to fire
   */
  public void fire() {
    // Code to fire
  }
}
The code needed to move the ship is taken care of in the Movable abstract class, so we can remove it from the Ship class. All the other objects for Asteroids could also inherit from the Movable class, making it extremely easy to change how to move an object. One thing to note about inheritance is the ability to have multiple inheritance, or the ability of one class to inherit from multiple classes at the same time. Some languages allow it, others do not. Java is one of the languages that does not allow multiple inheritance. Therefore, you couldn’t have the Ship object inherit from both a Moveable class and a Drawable class. Make sure you are familiar with what your programming language allows before you try to design inheritance into your game.

Tetris

Inheritance can be applied to Tetris by having the Tetrimino and all of the game visuals inherit from the Drawable class, which we defined in the last article.

Pac-Man

Recall that for Pac-Man we identified thee objects: Pac-Man, a Ghost, and a pac-dot. Throughout this series we’ve only discussed these three objects and have put off mentioning anything about the last critical piece of Pac-Man: the power pellet. With inheritance, we are now ready to talk about it. A power pellet is a special pac-dot that allows Pac-Man to eat ghosts. Its states and behaviors are exactly the same as a pac-dot, with really the only difference being its size and the ability to blink (remember that to keep the game loosely coupled, we want another class to monitor when a power pellet is eaten and active the changeState() method of the ghosts). This is when inheritance comes in handy. Since a pac-dot and power pellet are practically the same object, we can create a PowerPellet class which extends the PacDot class. The PowerPellet class would just need to modify a few states to make it bigger and add the behavior of growing and shrinking to create a blinking effect. And that’s it – we now have a power pellet with little extra work. Not too shabby. The code for how this would look could be as follows:
Read more: Quick Tip: The OOP Principle of Inheritance

No comments:

Post a Comment