Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Factory Method + Reflection: Achieving Better Extensibility in Applications

0.00/5 (No votes)
31 Aug 2008 1  
This article shows how to take advantage of the Factory Method design pattern and Reflection, to add more extensibility to your applications.

Introduction

This article attempts to show a 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 finding it! ;)

Problem

Suppose we have to design a money management application, where a user can have different types of accounts to keep track of his/her 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 information (maybe even store in a different database table,... depends on your application). For example, you may want to include for a bank account, 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 that information, 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 maintaining 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 an 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 that is what makes it 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 appropriately.

The solution is to use reflection to look at our program and inform the factory which account types we have. So the first thing is 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 we will read from it, the property we just create:

public static List<string> AccountTypeList()
{
    Assembly currentAssembly = Assembly.GetExecutingAssembly();
    List<string> list = new List<string>();
    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 many 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 them to the user (maybe by a combo like above), we just need to be able to create new instances of an 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 the classes are structured, etc. Also, try adding a new account class and see how it works...

Conclusion

In this article, we were able to create an 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 found to express the idea behind factory+reflection, but you can use it in several different opportunities 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!

History

  • 31st August, 2008: Initial post

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