Quick Tip: The OOP Principle of Cohesion

Quick Tip: The OOP Principle of Cohesion

Tutorial Details
  • Difficulty: Beginner
  • Estimated Completion Time: 15 minutes
This entry is part 2 of 6 in the series Beginner's Guide to OOP

In the first post of this series, we discussed why object-oriented programming (OOP) was helpful for game development, and learned how to identify objects, their states, and their behaviors. In this article, we’ll look at the specific OOP principle of cohesion and how it applies to games.

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 Cohesion?

Cohesion is the principle of being or doing one thing well. In other words, cohesion means grouping together code that contributes to a single task.

A great non-programming example of this principle was covered in one of the first Gamedevtuts+ articles which talked about the Covert Action Rule:

Don’t try to do too many games in one package … Individually, those each could have been good games. Together, they fought with each other.

The same rule applies to object-oriented programming. Each object should only have one responsibility. Every behavior of that object should only do one task. Any more than that and you’ll have a much harder time making changes to the code.


Why Is It Helpful?

Code that is organized by functionality and does only one task is said to have high cohesion. Highly cohesive code is reusable, simple, and easy to understand. It also creates objects that are small and focused.

Code that is organized arbitrarily and has multiple tasks is said to have low cohesion. Such code is difficult to understand, maintain, and reuse, and is often complex. It also creates objects that are large and unfocused.

Having high cohesion is generally good, while having low cohesion is generally bad. When writing code, always strive to write highly cohesive code.


How to Apply It

So how do we apply this to object-oriented programming? Well for starters, organizing code into objects helps increase cohesion of the game in general. However, each individual object should also have high cohesion. Let’s refer back to our three examples to see how this works.

Asteroids

Recall from the last article that we defined the ship object as having behaviors of turning, moving, and firing.

If we were to write a single piece of code that did all three behaviors at once, it would get pretty messy. Instead, we should separate each behavior into what are known as functions. Functions allow us to separate functionality and group similar code together, thus helping to create highly cohesive code.

In programming, an object is defined by creating a class. In Java, a class is coded as follows:

/**
* The Ship Class
*/
public class Ship {

  /**
   * Function – performs the behavior (task) of turning the Ship
   */
  public void rotate() {
    // Code that turns the ship
  }

  /**
   * Function – performs the behavior (task) of moving the Ship
   */
  public void move() {
    // Code that moves the ship
  }

  /**
   * Function – performs the behavior (task) of firing the Ship's gun
   */
  public void fire() {
    // Code that makes the ship fire a bullet
  }
}

As you can see, each behavior gets its own function, and the code is pretty well organized just in this skeleton structure.

Don’t worry too much about the exact syntax just yet; we’ll discuss it in more detail as we get further along in the series.

Tetris

For Tetris, recall that the behaviors of a tetromino were falling, moving (sideways), and rotating. The basic class structure is as follows:

/**
* The Tetromino Class
*/
public class Tetromino {

  /**
   * Function – update a Tetromino's position
   */
  public void fall() {
    // Code that updates the Tetromino's position
  }

  /**
   * Function - move a Tetromino
   */
  public void move() {
    // Code that moves the Tetromino sideways
  }

  /**
   * Function – rotate a Tetromino
   */
  public void rotate() {
    // Code that rotates the Tetromino by 90 degrees
  }
}

Again, the behaviors are separated into their own functions. For the fall method, though, notice that the task is to update the tetromino’s position. This is because the tetromino is always falling, so we can’t just make the task “cause the tetromino to fall”.

Instead, a falling tetromino just moves down the screen a certain number of rows at a time – so we have to update the position of the tetromino to reflect this falling speed.

Pac-Man

For the ghost object with behaviors of moving and changing state, we have to do a bit more work to get it to be highly cohesive.

/**
* The Ghost Class
*/
public class Ghost {

  /**
   * Function – moves the Ghost
   */
  public void move() {
    // Code that moves the ghost in the current direction
  }

  /**
   * Function - change Ghost direction
   */
  public void changeDirection() {
    // Code that changes the Ghost's direction
  }

  /**
   * Function – change Ghost speed
   */
  public void changeSpeed() {
    // Code that changes the Ghost's speed
  }

  /**
   * Function – change Ghost color
   */
  public void changeColor() {
    // Code that changes the Ghost's color
  }

  /**
   * Function – change Ghost state
   */
  public void changeState() {
    // Code that changes the Ghost's state
    // This function also will call the three functions of changeDirection, changeSpeed, and changeColor
  }
}

The Ghost state has three extra functions added to it: changeDirection, changeColor, and changeSpeed. These weren’t in our original behavior list because they aren’t behaviors. Instead, these functions are what are known as helper functions and are there to help us maintain high cohesion.

The behavior of changing state (what happens when Pac-Man eats a power pellet) requires three different tasks to be performed: turn deep blue, reverse direction, and move more slowly. To maintain cohesion, we don’t want one function to do all three of these tasks, so we divide them up into three subtasks that the function will call upon to complete its one main task.

The use of the word and when describing what a behavior/function does usually means we should create more than one function.


Conclusion

Cohesion is the principle of grouping like code together and ensure each function performs only a single task. Cohesion helps to create code that is maintainable and reusable.

In the next Quick Tip, we’ll discuss the principle of coupling and how it relates to cohesion. Follow us on Twitter, Facebook, or Google+ to keep up to date with the latest posts.

Series Navigation<< Quick Tip: Intro to Object-Oriented Programming for Game DevelopmentQuick Tip: The OOP Principle of Coupling >>
Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • http://www.facebook.com/vincent.piel.18 Vincent Piel

    Thx.
    I think a good rule worth too be mentionned here is that : ** you should be able to state in a clear, short phrase what aspect of the game the object will manage **.

    So it might be for instance : “This object is in charge of the blood particules”. And then a good practice is to going more into details step by step : “This object is charge of computing positions and display of blood particules emitted where a character dye”.

    If you can’t state clearly the purpose of the object, most likely you should work this out before going on.

    One way of getting an idea of the purpose of the object, is to write a ‘skeleton’ of the object, called an Interface. This is the process you show in the three examples. I happen quite often to find myself taking a paper and a pen (see wikipedia for a definition of those :=) ) to write an object Interface and ‘see if it works’.

    Advisory : what follows is a long example of a developer(me)’s though while Interfacing. Now you know.

    Expl : So ok, my player can move, shoot, loose life, dye. But… what if he founds a bonus ? –> i should add takeBonus. And a malus ? –> takeMalus. A gun ? takeGun. And now, it is becoming a mess… So i remove takeBonus/takeMalus/takeGun and i write takeItem. And in another part of the paper, i write an interface for an Item. And i see an item doesn’t do anything, rather it just has some properties. So in computer words, it is just a storage class. Using the classical RPG notation, it has is a set of { health, attack, defense, magic }. And for instance for a health bonus, we have health=+50. and if it was a gun, we have attack=+30. To display it on screen we need a draw() function, and to display it in inventory a getInventoryImage function. Now i begin to imagine the dynamic : when the user takeItem, he ‘applies’ the property of the item to him. when he takes a bonus with health=50 (all other values are 0) then the player should raise its health by 50. But weapons do not add up, so with a weapon of 30 attack, the player should not add, but rather set the attack to 30. And i keep on thinking about scenarios that can happen within the game, and adding/removing function or properties, and even creating/destroying other Interface, until i understand the place of each piece in the game.

    One last word : giving long, clear names to your properties (data) or functions (behaviour) is key to success ! imagine you find (and even worse : 3 month after you coded it) , in a file of your project, this wonderfull Interface ( case A), called : Level, with members : add, class, newClass, prop : ???? . And compare it with (case B) this interface, called : PlayerClassHandler, with members : levelUp, getCurrentClass, changeClass, getPlayerProperties. Which code would you prefer to deal with ? It is worth to take some time to find names that will makes the Interface obvious.

    Hopes this helps.

    • http://twitter.com/StevenKLambert Steven Lambert
      Author

      I really liked when you said that “if you can’t state clearly the purpose of the object, most likely you should work this out before going on.” You really should know exactly what an object’s purpose is before you attempt to code it. Doing so will help you write better and more maintainable code. Thanks for input.