LasaterConsulting.com

Design Patterns
Type-Object | Properties | Strategy | Entity Relationship | Interpreter | Project Usage | AOM UI

Factory & Strategy Pattern

Introduction

Developers in .NET sometimes come from scripting language environments that are not strong on Object Oriented Methodologies. OO methodologies like refactoring and using design patterns can be intimidating and the value for the developer is hard to see. What developers new to the OO world, and even more seasoned developers need to understand is that good design is not beyond scope in any project. Good habits simply must be learned that will make the developer a better designer.

In that mind set, I am submitting the first of several "how to" real world examples, used in my professional life, on how to use patterns in a simple, easy to follow manner. Many of us have ended up either writing or working on in-line, scripting based code. Taking some ideologies from refactoring methodology, this is not necessarily a bad thing. Of course, when you are first writing an algorithm or a series of logic statements, it seems easier to start with if....then....else. But as you continue to expand this code to more complex forms, this model can quickly become unmanageable. This is where refactoring and design pattern methodology can play an important and useful part in simplifying, enhancing and making code more useable (and understandable) to those who practice good OO design.

Using the code

This first effort will detail the use of a factory and several strategy patterns. I have a long series of if....then....else statements, that contain algorithms that can be broken into classes. These algorithms each have to use data from outside the statements, and based on this data will execute separate logic statements from similar data.

	
  if(i == 1){
		p = x + y;			
  }
  else if(i == 2){
			p = x * y;  		
  } else if(......
		//more algorithmic code.....

The variable i is the switch or deciding logic for which statement gets executed. Let us assume that the if....then....else block is quite long and has complex processing inside each statement. Let us also assume that i is a constant or a value that is not changeable outside of the parameters of the code.

The first logical step might be to extract the if....then....else algorithms out into classes, keeping in mind the rules of encapsulation, and making sure to pass into the class only the information which is absolutely needed. That is to say if you are passing around a heavy data object, but only use a small amount of data from the parameters from that object, it stands to reason that the constructor of your new classes (or methods on that class containing your logic) would not accept the whole object, but only those parameter variables values that were absolutely necessary, keeping in mind that no state was needed to be maintained on said object. This gives a more visible and obvious code flow, and prevents problems with unexpected modifications of the object or code.

But for this exercise, we are assuming that we are only passing around simple primitives, and no stateful object is implied. (If we were using a stateful object another design pattern might be more applicable, we will cover this in a later article.)

First, since all our algorithm classes need a base structure to allow them to be called in a similar fashion, we need to create a parent or abstract class. This class allows us to create a very similar way to call different classes that contain different algorithm logic in a similar manner. We will use this functionality later, to allow these classes, which are the beginnings of strategy pattern classes, to be created in a factory class, that can generate the code as we need to use it. The methods on the strategy classes will pass back an integer, which is the result of the algorithmic computation.

	public abstract class AbstractArithmiticStrategy
	{
		public abstract int DoArithmitic(int x, int y);

	
	}

Your strategy classes could pass back any data you wanted, access a database, modify a passed object somehow, or perhaps create different objects based on the algorithm. (for more information see the DoFactory site for PatternStrategy.) Generally though, in the strictest sense of the strategy pattern, your strategy should fulfill some logical work for whatever code is passed into it. Lets say each class from the if....then....else needs to perform simple arithmetic on the integer returned in a different way. Notice that we have inherited from AbstractArithmiticStrategy. This provides us with a way to call the same method when pulling from the factory (we will see the example later).

NOTE: Notice that we are using an attribute on each class. We will see in a little while why we are doing this.

	[AlgorithmAttribute(AlgorithmType.Addition)]
	public class AdditionArithmiticStrategy : AbstractArithmiticStrategy
	{
		public int DoArithmitic(int x, int y)
		{
			return x + y;
		}

	
	}


	[AlgorithmAttribute(AlgorithmType.Multiplication)]
	public class MultiplicationArithmiticStrategy : AbstractArithmiticStrategy
	{
		public int DoArithmitic(int x, int y)
		{
			return x * y;
		}

	
	}
	
	[AlgorithmAttribute(AlgorithmType.Subtraction)]

	public class SubtractionArithmiticStrategy : AbstractArithmiticStrategy
	{
		public int DoArithmitic(int x, int y)
		{
			return x - y;
		}

	
	}
	
	[AlgorithmAttribute(AlgorithmType.Division)]
	public class DivisionArithmiticStrategy : AbstractArithmiticStrategy
	{
		public int DoArithmitic(int x, int y)
		{
			return x / y;
		}

	
	}

Now we have broken our algorithm code out from the if....then....else and are only passing in the needed data, satisfying the requirements of good encapsulation. But we still need a way to decide how to render the right code at the right time. Since we know the variable i is always going to be a value we expect, lets convert the known values of i to an Enum. This will provide us with a structured way to pass the variable value to the factory, and allows the factory to decide on the value of the enum which algorithm class to procure to the executing code. We can also use an attribute class to help the factory decide which class type to render.

	enum AlgorithmType
	{
		Addition = 1;
		Subtraction = 2;
		Multiplication = 3;
		Division = 4;
	
	}


	[AttributeUsage(AttributeTargets.Class, AllowMultiple=false)]
	public class AlgorithmAttribute : Attribute
	{
		private AlgorithmType _algorithmType;

		
		public AlgorithmAttribute(AlgorithmType algorithmType)
		{
			this._algorithmType = algorithmType;
		}	
	
		public AlgorithmType TypeOfAlgorithm
		{
			get{return _algorithmType;}
		}	
	}

Here we have the factory code. As you can see we register our known algorithm classes with the factory at compile time. This is done to procure the class types, so we can use reflection typing to render our classes from the factory. We pass in the Enum AlgorithmType and check it against the attribute on each class.

	public class  AlgorithmStrategyFactory
	{
		private static ArrayList _registeredImplementations;
		
		static AlgorithmStrategyFactory()
		{
			_registeredImplementations = new ArrayList();
			RegisterClass(typeof(AdditionArithmiticStrategy));
			RegisterClass(typeof(MultiplicationArithmiticStrategy));
			RegisterClass(typeof(SubtractionArithmiticStrategy));
			RegisterClass(typeof(DivisionArithmiticStrategy));
		
		}

		public static void RegisterClass(Type requestStrategyImpl)
		{
			if (!requestStrategyImpl.IsSubclassOf(typeof(AbstractArithmiticStrategy)))
				throw new Exception("ArithmiticStrategy must inherit from class AbstractArithmiticStrategy");

			_registeredImplementations.Add(requestStrategyImpl);
		}

		public static AbstractArithmiticStrategy Create(AlgorithmType algorithmType)
		{
			// loop thru all registered implementations
			foreach (Type impl in _registeredImplementations)
			{
				// get attributes for this type
				object[] attrlist = impl.GetCustomAttributes(true);

				// loop thru all attributes for this class
				foreach (object attr in attrlist)
				{
					
					if (attr is AlgorithmAttribute)
					{
						if (((AlgorithmAttribute) attr).TypeOfAlgorithm.Equals(algorithmType))
						{
							return (AbstractArithmiticStrategy) System.Activator.CreateInstance(impl);
						}
					}
				}
			}

			throw new Exception("Could not find a AbstractArithmiticStrategy implementation for this AlgorithmType");
		}
	}

Now lets look at the functional code and compare it with the code we started with.

	
  int p = (AlgorithmStrategyFactory.Create(algorithmType)).DoArithmitic(x,y);

This code looks very different from what we started with. First it is one line. Second if we wished we could move this code to another area in which we needed the same processing easily, and expect the same functionality (based on the values of x and y). Of course there was a lot of work to get to this point, and when you are refactoring code, you have to evaluate whether doing refactoring in some areas is worth the effort. Remember design patterns are only templates or ways of designing code to make it more workable and maintainable, but you as the designer are going to have to decide what pattern if any works for you in different instances. Whenever you refactor, remember you should start with the smallest changes first, and make small refactorings, the sum of which lead to a better overall design of code.

Points of Interest

For more info on refactoring visit the Martin Fowler site at http://www.martinfowler.com/ . For more information on design patterns for c# visit www.dofactory.com.

If this article helps you develop better code or for any comments or suggestions, please e-mail me at chris.lasater@gmail.com, or post a comment here.

History

This is the first article submission in a series that I am writing to CodeProject on this subject and is the first revision. Look for other articles on design patterns coming soon.

Related Articles

Other articles in this series include:

  1. Bridge & Adapter Pattern
  2. Chain Of Responsibility Pattern
  3. Facade Pattern
  4. Factory & Strategy Pattern
  5. Mediator Pattern
| Make a Donation to Help Build/Maintain our site! |©2004 Lasater Consulting

1