Click here to Skip to main content
6,822,123 members and growing! (17,459 online)
Email Password   helpLost your password?
Development Lifecycle » Design and Architecture » Design and Strategy     Intermediate License: The Code Project Open License (CPOL)

Factory Method + Reflection : Achieving better extensibility in applications

By Caio Kinzel Filho

This article shows how to take advantage of the Factory Method design pattern and Reflection, to add more extensibility to your applications.
C#, Windows, .NET, Dev
Posted:31 Aug 2008
Views:13,320
Bookmarked:42 times
Unedited contribution
printPrint   add Share
      Discuss Discuss   Broken Article?Report  
13 votes for this article.
Popularity: 4.72 Rating: 4.24 out of 5

1
1 vote, 7.7%
2
1 vote, 7.7%
3
5 votes, 38.5%
4
6 votes, 46.2%
5
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!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Caio Kinzel Filho


Member

Occupation: Software Developer
Company: TOTVS S/A
Location: Brazil Brazil

Other popular Design and Architecture articles:

Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
 Msgs 1 to 21 of 21 (Total in Forum: 21) (Refresh)FirstPrevNext
Generalimplementation of Factory method pattern or Factory pattern? Pinmember100rabhSaxena22:10 26 Mar '09  
GeneralRe: implementation of Factory method pattern or Factory pattern? PinmemberCaio Kinzel Filho8:09 27 Mar '09  
GeneralInteresting, but I think it could be even better. Pinmemberwtwhite18:08 2 Sep '08  
GeneralRe: Interesting, but I think it could be even better. PinmemberCaio Kinzel Filho6:18 3 Sep '08  
GeneralRe: Interesting, but I think it could be even better. Pinmemberwtwhite7:11 3 Sep '08  
GeneralRe: Interesting, but I think it could be even better. PinmemberCaio Kinzel Filho8:20 3 Sep '08  
GeneralWhy not use DI/IoC? PinmemberS. M. SOHAN18:19 1 Sep '08  
GeneralRe: Why not use DI/IoC? PinmemberCaio Kinzel Filho19:16 1 Sep '08  
GeneralRe: Why not use DI/IoC? PinmemberS. M. SOHAN19:47 1 Sep '08  
GeneralConfig File Pinmemberfreaklord5:06 1 Sep '08  
GeneralRe: Config File PinmemberCaio Kinzel Filho5:20 1 Sep '08  
GeneralLINQ PinmemberPawel Gielmuda4:07 1 Sep '08  
GeneralLINQ PinmemberCaio Kinzel Filho4:17 1 Sep '08  
GeneralRe: LINQ PinmemberPawel Gielmuda0:05 2 Sep '08  
GeneralNice, Pinmemberring_01:50 1 Sep '08  
GeneralRe: Nice, PinmemberCaio Kinzel Filho4:12 1 Sep '08  
QuestionSome questions... PinmemberItay Sagui23:08 31 Aug '08  
AnswerRe: Some questions... PinmemberCaio Kinzel Filho3:54 1 Sep '08  
GeneralNice. Pinmemberanindak16:58 31 Aug '08  
GeneralRe: Nice. PinmemberCaio Kinzel Filho19:34 31 Aug '08  
GeneralRe: Nice. PinmemberStanislav Georgiev23:55 2 Sep '08  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

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

PermaLink | Privacy | Terms of Use
Last Updated: 31 Aug 2008
Editor:
Copyright 2008 by Caio Kinzel Filho
Everything else Copyright © CodeProject, 1999-2010
Web21 | Advertise on the Code Project