Advertisement

Quick Tip: The OOP Principle of Cohesion

by

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.

Advertisement