Click here to Skip to main content
15,881,757 members
Articles / Programming Languages / C#
Article

Refactoring to Patterns: Factory and Strategy Patterns

Rate me:
Please Sign up or sign in to vote.
3.59/5 (19 votes)
25 Mar 20057 min read 72.6K   58   9
This is a hands on article on how to refactor to design patterns, in specific for this article, factory and strategy design patterns.

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 use data from outside the statements, and based on this data will execute separate logic statements from similar data.

C#
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 the 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 those information which is absolutely needed. That is to say that if you are passing around a heavy data object, but use only a small amount of data from the parameters of that object, it stands to reason that the constructors of your new classes (or methods on that class containing your logic) would not accept the whole object, but only those parameter variable values that were absolutely necessary, keeping in mind that no state was needed to be maintained on the 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 another 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.

C#
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. Let's 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.

C#
[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, let's 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:

C#
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:

C#
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 let's look at the functional code and compare it with the code we started with:

C#
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 the 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

This is the first installment in the series I am writing on real world design patterns. All examples and the bulk of this article are taken from my professional experience as an architect. The examples given are templates only, and the designer must keep in mind that they are the ones who must decide where different patterns, if any, may be best used in their code.

Deciding to perform a refactoring effort from the existing code to a pattern must be weighed on the necessity and need of the code itself. Patterns are only design templates, helpers to accommodate better overall design. I must stress that making an effort to use patterns will strengthen your overall design ability, but like your basic coding skills, it is something that is to be learnt and cultivated.

If this or any other in this series on design patterns is helpful or you have questions or comments please e-mail me at chris.lasater@gmail.com.

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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
Christopher G. Lasater

I am also a published author, please check out my book:
ISBN: 1-59822-031-4
Title: Design Patterns
Author:Christopher G. Lasater
More from my web site
Amazon.com


Comments and Discussions

 
GeneralMy vote of 5 Pin
kamaljasra7-Dec-10 20:23
kamaljasra7-Dec-10 20:23 
GeneralHeavy Stuff Pin
kamarchand3-Apr-07 4:03
kamarchand3-Apr-07 4:03 
GeneralSimple question Pin
M i s t e r L i s t e r10-Oct-06 12:29
M i s t e r L i s t e r10-Oct-06 12:29 
GeneralRe: Simple question Pin
Christopher G. Lasater11-Oct-06 4:52
Christopher G. Lasater11-Oct-06 4:52 
GeneralGood stuff - please include image Pin
Tommi G18-Nov-05 19:31
Tommi G18-Nov-05 19:31 
This is good. I've read about design patterns, but one really needs most of times a working implementation with a familiar language to see how the patterns actually form. This is good instead of reading about this stuff in some c++ or pseudo language. Thanks.

It would be great if you included image about the pattern as you did in your other articles.
GeneralRe: Good stuff - please include image Pin
Christopher G. Lasater20-Nov-05 6:35
Christopher G. Lasater20-Nov-05 6:35 
GeneralRefactoring to Patterns: Factory and Strategy Patterns Pin
Christopher G. Lasater29-Mar-05 4:37
Christopher G. Lasater29-Mar-05 4:37 
GeneralThanks. Pin
WebMaster27-Mar-05 22:05
WebMaster27-Mar-05 22:05 
GeneralRe: Thanks. Pin
Christopher G. Lasater28-Mar-05 4:24
Christopher G. Lasater28-Mar-05 4:24 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.