Archive for February, 2010

Enums Evolved

Friday, February 19th, 2010

Keying on a particular value is one of the first things we learn in programming.  As time goes on and we learn more advanced concepts, sometimes the little things tend to get lost in the dust and we keep doing things the same way we learned them in grade one.  I recently experienced a little of this pain in a bin packing project I’d been working one.  It all stared with enums, but for the sake of completeness I’m going to start all the way back with strings because that is probably what we all cut our teeth on.

The scenario is this. In a cuboid (box) that is axis aligned we will only ever have six surfaces. In writing code to deal with the cuboid we will be working with these surfaces and we need to know which way that surface is pointing.  Using a simple string approach we might represent it like this…

public class Surface : Rectangle
{
	public static string OrientationBack = "Back";
	public static string OrientationFront = "Front";
	public static string OrientationLeft = "Left";
	public static string OrientationRight = "Right";
	public static string OrientationUp = "Up";
	public static string OrientationDown = "Down";
 
	public string Orientation { get; set; }
}

Now we have a string that tells us the direction that the surface we are working on is pointing. A better approach to the string method would be to put the strings in their own class.

public class Orientation
{
	public static string Back = "Back";
	public static string Front = "Front";
	public static string Left = "Left";
	public static string Right = "Right";
	public static string Up = "Up";
	public static string Down = "Down";	
}
 
public class Surface : Rectangle
{
	public string Orientation { get; set; }
}

The string approach is not optimal for several reasons. 1) We know that the types of orientation are never going to change and so representing orientation as a string is dangerous because if a bad string is used then our logic will not be able to account for it. 2) String comparison while not exactly slow, could be improved by another type. This type of problem would be much better served by an enum instead.

public enum Orientation
{
	Back,
	Front,
	Left,
	Right,
	Up,
	Down	
}
 
public class Surface : Rectangle
{
	public Orientation Orientation { get; set; }
}

This is better because we can guarantee that only our six types will ever be used as an orientation. Plus because enum types are based on integers, comparison operations are fast.

Now we need to do something based on which direction our surface is pointing. This situation appears because our surface is anchored in 3d space by a point which represents its upper left hand corner. We also have its width and height. However in order to do faster collision detection, rendering and ordering we need to translate those values into absolute coordinates for the axis that our surface is sitting on. This means that if we have a surface that is facing left, our depth is going to be on the X axis, our height will be on Z axis, and our width will be Y axis of the 3d container space. This will change depending on which side we are working with. Don’t worry if this doesn’t make sense, it’s simply an example to help illustrate our problem. We are going add these properties to our surface class.

/// <summary>
/// The depth of this plane on the Axis it is aligned to 
/// </summary>
public decimal Z { get; private set; }
 
/// <summary>
/// Top of this surface on the axis it is aligned to
/// </summary>
public decimal Top { get; private set; }
 
/// <summary>
/// Bottom of this surface on the axis it is aligned to
/// </summary>
public decimal Bottom { get; private set; }
 
/// <summary>
/// Left side of this surface on the axis it is aligned to
/// </summary>
public decimal Left { get; private set; }
 
/// <summary>
/// Right side of this surface on the axis it is aligned to
/// </summary>
public decimal Right { get; private set; }

We need to populate those values if our orientation changes or if our point changes. Using the string, or enum method we would end up with something like this.

private void TranslatePoint(Point point)
{
	switch(Orientation)
	{
		case Orientation.Back:
			Z = point.Y;
			Left = point.X;
			Top = point.Z;
			Right = Left - Width;
			Bottom = Top - Height;
			break;
		case Orientation.Front:
			Z = point.Y;
			Left = point.X;
			Top = point.Z;
			Right = Left + Width;
			Bottom = Top - Height;
			break;
		case Orientation.Left:
			Z = point.X;
			Left = point.Y;
			Top = point.Z;
			Right = Left + Width;
			Bottom = Top - Height;
			break;
		case Orientation.Right:
			Z = point.X;
			Left = point.Y;
			Top = point.Z;
			Right = Left - Width;
			Bottom = Top - Height;
			break;
		case Orientation.Up:
			Z = point.Z;
			Left = point.X;
			Top = point.Y;
			Right = Left + Width;
			Bottom = Top + Height;
			break;
		case Orientation.Down:
			Z = point.Z;
			Left = point.X;
			Top = point.Y;
			Right = Left + Width;
			Bottom = Top - Height;
			break;		
	}
}

Wow, that’s a lot of bulk. Now there are probably certain things that could be done to trim that method, but it would still be bulky and hard to read. Plus I am sure that I will be running into the same problem in other places. When you see a method like this you know that it is time to look for refactoring options. There must be a way to move the logic out of this method into the Orientation where it belongs.

There is, and it is called the strategy pattern. Using the strategy pattern we can move the logic into an Orientation class. However because we only want a set amount of values we are going to make some modifications to make it more “enum” like.

Let’s create a new class called Orientation. It will be used both to represent the orientation and to translate global points to that orientation or perform operations that pertain to that orientation.

public class Orientation
{
	private readonly Func<Point, Point> _translator;
	private readonly MathOp _increaseX;
	private readonly MathOp _increaseY;
	private readonly MathOp _increaseZ;
	private readonly Func<Orientation> _flipside;
 
	/// <summary>
	/// Creates a new orientation
	/// </summary>
	/// <param name="translator">Method to translate a global point into a local point</param>
	/// <param name="increaseX">Math operation necessary increase X values</param>
	/// <param name="increaseY">Math operation necessary increase X values</param>
	/// <param name="increaseZ">Math operation necessary increase X values</param>
	/// <param name="flipside">The flipside of this orientation</param>
	private Orientation(Func<Point, Point> translator, 
		MathOp increaseX, 
		MathOp increaseY, 
		MathOp increaseZ,
		Func<Orientation> flipside)
	{
		if (translator == null) throw new ArgumentNullException("translator");
		if (increaseX == null) throw new ArgumentNullException("increaseX");
		if (increaseY == null) throw new ArgumentNullException("increaseY");
		if (increaseZ == null) throw new ArgumentNullException("increaseZ");
		if (flipside == null) throw new ArgumentNullException("flipside");
		_translator = translator;
		_increaseX = increaseX;
		_increaseY = increaseY;
		_increaseZ = increaseZ;
		_flipside = flipside;
	}
 
	/// <summary>
	/// The flipside of this orientation
	/// </summary>
	public Orientation FlipSide
	{
		get { return _flipside(); }
	}
 
	/// <summary>
	/// Translates a global point to this orientation
	/// </summary>
	/// <param name="point"></param>
	/// <returns></returns>
	public Point Translate(Point point)
	{
		return _translator(point);
	}
 
	public MathOp IncreaseX
	{
		get { return _increaseX; }
	}
 
	public MathOp IncreaseY
	{
		get { return _increaseY; }
	}
 
	public MathOp IncreaseZ
	{
		get { return _increaseZ; }
	}
}

Notice how the constructor is private so that we can control its instantiation. We are also making use of delegates in the constructor so we can easily create logic in the constructor without having to resort to making a subclass for each different orientation. If you notice that we havn’t defined the MathOp class anywhere, that’s because it’s simply this exact same pattern repeated except for a math operation.

Next we are going to infuse the strategy pattern with enum like functionality through the use of static fields.

public class Orientation
{
	public static Orientation Back = new Orientation(
		p => new Point(p.X, p.Z, p.Y), 
		MathOp.Subtract, 
		MathOp.Subtract, 
		MathOp.Subtract, 
		() => Front);
	public static Orientation Front = new Orientation(
		p => new Point(p.X, p.Z, p.Y), 
		MathOp.Add, 
		MathOp.Subtract, 
		MathOp.Add, 
		() => Back);
	public static Orientation Left = new Orientation(
		p => new Point(p.Y, p.Z, p.X), 
		MathOp.Add, 
		MathOp.Subtract, 
		MathOp.Subtract, 
		() => Right);
	public static Orientation Right = new Orientation(
		p => new Point(p.Y, p.Z, p.X), 
		MathOp.Subtract, 
		MathOp.Subtract, 
		MathOp.Add, 
		() => Left);
	public static Orientation Up = new Orientation(
		p => new Point(p.X, p.Y, p.Z), 
		MathOp.Add, 
		MathOp.Add, 
		MathOp.Add, 
		() => Down);
	public static Orientation Down = new Orientation(
		p => new Point(p.X, p.Y, p.Z), 
		MathOp.Add, 
		MathOp.Subtract, 
		MathOp.Subtract, 
		() => Up);

We have now ended up with an enum-like class that incorporates so much more functionality. Assigning an orientation to a variable is as simple as…

surface.Orientation = Orientation.Up;

Comparison works the same way.

if(Orientation == Orientation.Up)
	//do something

The only thing that we are lacking is the ability to switch on our Orientation class, but with the new functionality we shouldn’t need to. Just look at what our bulky “TranslatePoint” method has turned into.

private void TranslatePoint(Point point)
{
	Point translated = Orientation.Translate(point);
	Z = translated.Z;
	Left = translated.X;
	Top = translated.Y;
	Right = Orientation.IncreaseX.Calculate(Left, Width);
	Bottom = Orientation.IncreaseY.Calculate(Top, Height);
}

By applying an appropriate pattern we have made our code more readable, less bulky and insured that it has a stable growth plan.

Remember when refactoring don’t forget the simple things.