Knock Down The If-ful Tower

Novice coders, as well as intermediate coders who are simply in a rush often end up with code structures like these:

public function importantLogic(player:Player):void {
 
	if (player.isMoving) {
		if (player.isJumping) {
			// do something with player...
			//...
			//...
		}else if (player.isAboutToJump) {
			// do something with player...
			//...
			//...
		}
	}else if (player.isCrouching) {
		// do something with player...
		//...
		//...
	}else if (player.isAboutToMove) {
		//...
		//...
	}else if (player.isAboutToJump) {
		//...
		//...
	}
 
 
 
}

I call these structures “If-ful Towers” (pronounced like “Eiffel Towers” without the leading “E”). Obviously there is nothing wrong with the humble if-else statement, or its cousin if-else-if. Our code would hardly work without branching logic, after all. One of my co-workers and I were talking about this sort of structure and how it’s highly indicative of the “Broken Windows” syndrome discussed in “The Pragmatic Programmer“. How do you maintain code that is structured this way? It already looks imposing, and there’s not even any real code written in the blocks.

If you look closely at this particular code snippet, you’ll notice that we’re testing the state of the Player instance. The Player clearly can exhibit a great number of states. Most of them are simple boolean properties. Many of those properties are mutually exclusive. Who knows what sort of havoc could happen if somehow the player thinks that it isMoving, and isCrouching, and isJumping, and isAboutToMove all at the same time? Couldn’t we knock down the If-ful Tower if we were to just be a little smarter about how we determine the state of the Player?

package {
 
	/**
	 * ...
	 * @author The Horseman @ Scriptocalypse.com
	 * 
	 *   Here we have a base class for a certain set of actions to perform.  
	 *   We can create many different subclasses, such as....
	 * 
	 *       WalkingContext
	 *       CrouchingContext
	 *       JumpingContext
	 */
	public class Context {
 
		public function Context() {
 
		}
 
 
		// override in your subclass...
		public function execute(player:Player, foo:*):void {
			// player is the Player object.
			// foo is some data that player should be concerned with.
		}
 
	}
 
}
 
 
 
 
 
// And here's the very basics of Player...
package {
 
	/**
	 * ...
	 * @author The Horseman @ Scriptocalypse.com
	 * 
	 *   The player has a context that is called upon to execute
	 *   on command.  Since there is only one context active at
	 *   any given time, we can be assured that as long as we 
	 *   supply the *one* correct context that the action taken
	 *   will be expected and desired.
	 * 
	 */
	public class Player extends MovieClip{
 
		private var _context:Context;
		public function get context():Context{
			return _context;
		}
 
		public function set context(value:Context):void{
			_context = value;
		}
 
 
 
		public function Player() {
 
		}
 
 
	}
 
}

Now it is completely unambiguous precisely which “state” the Player instance happens to be in at any given moment. There can be only one! As long as you have correctly assigned the context, simply calling upon it to execute should be all you need to do. Where once stood the If-ful Tower, we now have this:

public function importantLogic(player:Player):void {
 
	// That's it.  
	player.context.execute(player, 10);
 
 
 
 
}

Wow. Things just got a lot more readable in this code block. This is of course not the only way to knock down the If-ful Tower, and depending on your circumstances there may be better ways. Indeed, the player doesn’t necessarily need to have a reference to the Context object. That could perhaps be stored elsewhere, and simply accessed when needed.

Perhaps you need to implement collision handling between Player and an Enemy? It’s probably tempting to just build up yet another If-ful Tower replete with deeply nested logic and branches… but that’s a tower to topple another day!

Tags: , ,

  1. #1 written by Benjamin April 2nd, 2010 at 09:54

    Thanks for the article very helpful. Yeah I allows find myself with these long If or Switch statements for object states. Would this be an example of implementing a State Machine approach?

  2. #2 written by The Horseman April 3rd, 2010 at 06:55

    I don’t think you would call it a “state machine” in the strict sense, though it’s got some similarities. It also bears some similarity to the Strategy pattern in GoF, but this example only illustrates using a single “player” rather than all the enemies or actors in the program.