Skip to main content
Email Password   helpLost your password?
Download FactoryMethod_Reflection.zip - 71.1 KB

Introduction

This article attempts to show an usage of the Factory Method design pattern in combination with reflection to achieve extensibility to our application.

In order to illustrate it, we will use an example of creation of accounts for a money management application.

Background

It's not the intention of this article to teach how the Factory Method pattern works, nor the power/features of reflection, instead, we want to demonstrate how to take advantage of them together. If you need to understand any of these two concepts, you may want to search for it first, the Internet is full of materials about it, so you will not have problems to find it! ;)

Problem

Suppose we have to design a money management application, where a user can have different types of accounts to keep track of its money (you can think of Microsoft Money, Quicken, ...). The different types of accounts could be for example (among many others):

- Bank Account;
- Credit Card;
- Investment Account;
- Loan;
- Retirement Account;
- Stock Options;
- Wallet;
- Under the Mattress;
- etc.

For each of the account types, we have to ask the user for specific informations (maybe even store in a different database table,...depends on your application). For example, you may want to include for a bank acount, information regarding account number, opening date, bank identifier, etc., information that will be pointless for a Wallet account.

So, the problem here is to efficiently ask the user to enter the information for the accounts, and do that in a way we could easily add new account types if needed.

Step 1: Initial Design

Starting our way up to better code, the first approach would be to create an Account class, and add a AccountType property to it. In this way we will have create in this class, properties to all information possible to all kinds of accounts, and when we need to prompt for those informations we do something like the following:

    public void PromptForDetails()
    {
        Account account = new Account();
        
        account.AccountType = comboSelectedType;

        if (account.AccountType == AccountTypeEnum.BankAccount)
        {
            //Show bank account details window
        }
        else if (account.AccountType == AccountTypeEnum.InvestmentAccount)
        {
            //Show investment account details window
        }
        //...
    }

With this, we will have to deal with creating and maintaing the enumeration for the account types, filling the combos, etc...

Step 2: Different Classes for Different Account Types

Well, improving our code we now add one different class for each account type, and put a method to prompt for details in each:

    public class BankAccount
    {
        public void PromptForDetails()
        {
            //Show bank account details window
        }
    }
    public class InvestmentAccount
    {
        public void PromptForDetails()
        {
            //Show investment account details window
        }
    }
    //...

Better yet, we create a superclass our account classes could inherit methods from:

    public abstract class AccountBase
    {
        public abstract void PromptForDetails();
    }
    public class BankAccount : AccountBase
    {
        public override void PromptForDetails()
        {
              //Show bank account details window
        }
    }
    public class InvestmentAccount : AccountBase
    {
        //Show investment account details window
    }
    //...

But we will still have to prompt for details like this:

    public void PromptForDetails()
    {
        Account account;

        if (comboSelectedType == AccountTypeEnum.BankAccount)
        {
            account = new BankAccount();
        }
        else if (comboSelectedType == AccountTypeEnum.InvestmentAccount)
        {
            account = new InvestmentAccount();
        }
        //...
        account.PromptForDetails();
    }

Now we could take care of the prompting for account details in each of our classes, and reuse it along the application. But we still have to create the enum, fill the combo and create a if block to verify which type was selected (and all these places will have to be modified if a new account type is added).

Step 3: Now with the Factory Method + Reflection

So, the idea here is to make the program decide which account type we want. That's where the Factory Method design pattern comes in place. The problem we will still have is that we have to make the factory aware of which types of accounts we have available, and its what make difficult for us at the time we want to add more account types in the system, because we will need to go in the factory class and change it apropriately.

The solution is to use reflection to look at our program and inform the factory which account types we have. So first thing, how do we know every account type we have?

To do that we will need to create a property in each account class, to inform how we want the type to be called. For that, let's create the property "AccountType":

    public class BankAccount : AccountBase 
    {
        public static String AccountType
        {
            get { return "Bank Account"; }
        }        
        //...
    }
    public class InvestmentAccount : AccountBase 
    {
        public static String AccountType
        {
            get { return "Investment Account"; }
        }        
        //...
    }
    

Now we will use reflection to find every class that inherits from "AccountBase", and will read from it, the property we just create:

    public static List AccountTypeList()
    {
        Assembly currentAssembly = Assembly.GetExecutingAssembly();
        List list = new List();
        Type[] types;

        types = currentAssembly.GetTypes();

        foreach (Type currentType in types)
        {
            //Verifies it the type subclasses AccountBase
            if (currentType.BaseType != null && currentType.BaseType.Name.Contains("AccountBase"))
            {
                try
                {
                    String accountType;
                    accountType = currentType.GetProperty("AccountType",
                        BindingFlags.Static | BindingFlags.Public).GetValue(null, null).ToString();

                    if (!String.IsNullOrEmpty(accountType))
                    {
                        list.Add(accountType);
                    }
                }
                catch (Exception ex)
                {
                    //...
                }
            }
        }

        //Return the list, sorted
        list.Sort(delegate(string s1, string s2) { return s1.CompareTo(s2); });
        return list;
    }

Ok, until here we have one benefit already, we could much more easily fill a combo for account type for example, and may no more need enums. Note that the code below to fill a combo, will never have to change, no matter how much new account classes you add:

    combo.Items.Clear();
    foreach (String s in AccountTypeList())
    {
        combo.Items.Add(s);
    }

Knowing all the account types, and how to present it to user (maybe by a combo like above), we just need to be able to create new instances of a account of the type we want by our factory. Similarly to looking for the list of account types, we create or Factory Method like this:

   public static AccountBase FactoryMethod(string accountTypeDesired)
    {
        Assembly currentAssembly = Assembly.GetExecutingAssembly();
        Type[] types;

        types = currentAssembly.GetTypes();

        foreach (Type currentType in types)
        {
            //Verifies it the type subclasses AccountBase
            if (currentType.BaseType != null && currentType.BaseType.Name.Contains("AccountBase"))
            {
                try
                {
                    String accountType;
                    accountType = currentType.GetProperty("AccountType",
                        BindingFlags.Static | BindingFlags.Public).GetValue(null, null).ToString();

                    if (accountType.Trim() == accountTypeDesired.Trim())
                    {
                        //return a new instance of the account
                        return (AccountBase)Activator.CreateInstance(currentType);
                    }
                }
                catch (Exception ex)
                {
                    //...
                }
            }
        }
        return null;
    }

Well, that's it! Now our code to create and prompt for accounts will simple be (and will never need to change as we add new account types):

    String selectedAccountType = comboAccountTypes.Text;
    if (!String.IsNullOrEmpty(selectedAccountType))
    {
        AccountBase account = AccountFactory.FactoryMethod(selectedAccountType);
        account.PromptForDetails();
    }

    

Sample Project

We encourage you to download the sample project and see how it's done, see how tha classes are structured, etc. Also, try adding a new account class and see how it works...

Conclusions

In this article we were able to create a extensible application to manage different types of accounts. Each new type of account could be easily added to the system by just adding a new class that subclasses the account base class. In this article, the accounts example was just a good way I find to express the idea behind factory+reflection, but you can use it in several different oportunities in your own projects, and of course, as more 'changeable' the feature you have is, more you will benefit from using the approach present here. Anyway, hope it will be of any use for some of you. Thank you for reading!

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
Generalimplementation of Factory method pattern or Factory pattern? Pin
100rabhSaxena
22:10 26 Mar '09  
GeneralRe: implementation of Factory method pattern or Factory pattern? Pin
Caio Kinzel Filho
8:09 27 Mar '09  
GeneralInteresting, but I think it could be even better. Pin
wtwhite
18:08 2 Sep '08  
GeneralRe: Interesting, but I think it could be even better. Pin
Caio Kinzel Filho
6:18 3 Sep '08  
GeneralRe: Interesting, but I think it could be even better. Pin
wtwhite
7:11 3 Sep '08  
GeneralRe: Interesting, but I think it could be even better. Pin
Caio Kinzel Filho
8:20 3 Sep '08  
GeneralWhy not use DI/IoC? Pin
S. M. SOHAN
18:19 1 Sep '08  
GeneralRe: Why not use DI/IoC? Pin
Caio Kinzel Filho
19:16 1 Sep '08  
GeneralRe: Why not use DI/IoC? Pin
S. M. SOHAN
19:47 1 Sep '08  
GeneralConfig File Pin
freaklord
5:06 1 Sep '08  
GeneralRe: Config File Pin
Caio Kinzel Filho
5:20 1 Sep '08  
GeneralLINQ Pin
Pawel Gielmuda
4:07 1 Sep '08  
GeneralLINQ Pin
Caio Kinzel Filho
4:17 1 Sep '08  
GeneralRe: LINQ Pin
Pawel Gielmuda
0:05 2 Sep '08  
GeneralNice, Pin
ring_0
1:50 1 Sep '08  
GeneralRe: Nice, Pin
Caio Kinzel Filho
4:12 1 Sep '08  
QuestionSome questions... Pin
Itay Sagui
23:08 31 Aug '08  
AnswerRe: Some questions... Pin
Caio Kinzel Filho
3:54 1 Sep '08  
GeneralNice. Pin
anindak
16:58 31 Aug '08  
GeneralRe: Nice. Pin
Caio Kinzel Filho
19:34 31 Aug '08  
GeneralRe: Nice. Pin
Stanislav Georgiev
23:55 2 Sep '08  


Last Updated 31 Aug 2008 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2009