Click here to Skip to main content
Click here to Skip to main content

Delegate Factory

By , 10 Jan 2005
Rate this:
Please Sign up or sign in to vote.

Introduction

The target of this article is to show another factory implementation using delegates. I've searched CodeProject for a similar implementation and though some are close they're not quite the same. So I've decided to post this factory which I use in my current project.

This article does not aim at explaining the whole concept of factory or why you would use that particular design pattern.

Table of contents

Goals

The goal in writing this factory are as follows:

  1. Dynamic factory - It will be possible in the future to use the same factory for new types of objects without opening the factory's code.
  2. Creation responsibility - The factory should not know how to create the objects.
  3. Ease of use.

Using a delegate

Using delegates will help us achieve goals 1 and 2, it might set us on a collision course with goal 3 but we'll see.

The declaration of the delegate looks like this:

/// <span class="code-SummaryComment"><SUMMARY>
</span>
/// A handler function for the factory to create objects;
/// <span class="code-SummaryComment"></SUMMARY>
</span>
public delegate AObject ObjectCreator(params object [] list);

This delegate will help us obscure the way objects are created from the factory and help us register delegates of this type on the factory.

Implementing the object hierarchy

The factory will create our first usable object in our tree, so that we don't have to downcast straight away (only if it's really needed).

The base class is abstract and defines one variable member and one abstract function:

/// <span class="code-SummaryComment"><SUMMARY>
</span>
/// Summary description for AObject.
/// <span class="code-SummaryComment"></SUMMARY>
</span>
public abstract class AObject
{
    #region Override

    /// <span class="code-SummaryComment"><SUMMARY>
</span>
    /// Force all descendents to implement.
    /// <span class="code-SummaryComment"></SUMMARY>
</span>
    public abstract void Print();

    #endregion

    #region protected

    /// <span class="code-SummaryComment"><SUMMARY>Some variable to play with.</SUMMARY>
</span>
    protected Int32 m_nType;

    #endregion
}

The derived classes look like this:

/// <span class="code-SummaryComment"><SUMMARY>
</span>
/// Summary description for Class1.
/// <span class="code-SummaryComment"></SUMMARY>
</span>
public class Class1 : AObject
{
    #region Constants

    /// <span class="code-SummaryComment"><SUMMARY>
</span>
    /// The class type identifier.
    /// <span class="code-SummaryComment"></SUMMARY>
</span>
    public const Int32 ClassType = 1;

    #endregion
        
    #region C'tor

    /// <SUMMARY>
    /// Default C'tor.
    /// <span class="code-SummaryComment"></SUMMARY>
</span>
    internal Class1()
    {
        this.m_nType = ClassType;
    }

    #endregion

    #region Overrides

    /// <span class="code-SummaryComment"><SUMMARY>
</span>
    /// Implementation of Print.
    /// <span class="code-SummaryComment"></SUMMARY>
</span>
    public override void Print()
    {
        String msg = String.Format("Class: {0, 20} Value: {1, 10}",
                                          ToString(), m_nType*67);
        Console.WriteLine(msg);
    }

    #endregion

    #region Static

    /// <span class="code-SummaryComment"><SUMMARY>
</span>
    /// A handler function for the factory to create objects;
    /// <span class="code-SummaryComment"></SUMMARY>
</span>
    /// The parameter list.
    /// <span class="code-SummaryComment"><RETURNS>A Class1 object.</RETURNS>
</span>
    public static AObject ObjectCreator(params object[] list)
    {
        return new Class1();
    }

    #endregion
}

As you can see we implemented a Print function for the derived that does something (hopefully something different than other derived) and we've added a static function that creates a new object of Class1.

The static function ObjectCreator keeps the responsibility of creating objects within the class.

The factory

The factory class maps the classes' static functions, with the help of a delegate to the type that we wish to create.

The map is done through a hashtable with the type of the object being the key and the delegate that encapsulates a static function as the value.

The factory:

/// <span class="code-SummaryComment"><SUMMARY>
</span>
/// Summary description for ObjectFactory.
/// <span class="code-SummaryComment"></SUMMARY>
</span>
public class ObjectFactory
{
    #region Static

    /// <span class="code-SummaryComment"><SUMMARY>
</span>
    /// Register handler functions to create new types of objects.
    /// <span class="code-SummaryComment"></SUMMARY>
</span>
    /// The type of the object.
    /// The handler function.
    /// <span class="code-SummaryComment"><RETURNS>true if successful.</RETURNS>
</span>
    public static bool RegisterHandler(Int32 type, ObjectCreator creator)
    {
        bool res = false;
        try
        {
            if (m_handlers[type] != null)
                return false;
            // insert the handler to the table according to the type.
            m_handlers[type] = creator;
            res = true;
        }
        catch(Exception ex)
        {
            Console.WriteLine("Can't register handler - "+ex.Message);
        }
        return res;
    }

    /// <span class="code-SummaryComment"><SUMMARY>
</span>
    /// Unregister handler functions according to type.
    /// <span class="code-SummaryComment"></SUMMARY>
</span>
    /// The type of the object.
    /// <span class="code-SummaryComment"><RETURNS>true if successful.</RETURNS>
</span>
    public static bool UnregisterHandler(Int32 type)
    {
        bool res = true;
        try
        {
            if (m_handlers[type] == null)
                return res;
            // remove the handler to the table according to the type.
            m_handlers[type] = null;
            GC.Collect();
        }
        catch(Exception ex)
        {
            Console.WriteLine("Can't unregister handler - "+ex.Message);
            res = false;
        }
        return res;
    }

    /// <span class="code-SummaryComment"><SUMMARY>
</span>
    /// This is the static method that creates all types of objects.
    /// <span class="code-SummaryComment"></SUMMARY>
</span>
    /// <span class="code-SummaryComment"><REMARKS>Factory method.</REMARKS>
</span>
    /// The key of objects to create.
    /// The parameter list for the object.
    /// <span class="code-SummaryComment"><RETURNS>An object.</RETURNS>
</span>
    public static AObject CreateObject(Int32 type, params object [] list)
    {
        AObject aobject = null;
        try
        {
            // get the handler that creates the objects
            ObjectCreator creator = (ObjectCreator)m_handlers[type];
            // create the object with the handler.
            if (creator != null)
                aobject = creator(list);
        }
        catch(Exception ex)
        {
            Console.WriteLine("Can't get object from handler - "+ex.Message);
        }
        return aobject;
    }

    #endregion

    #region Protected

    /// <span class="code-SummaryComment"><SUMMARY> A table holding the handlers for creating objects. </SUMMARY>
</span>
    protected static Hashtable m_handlers = new Hashtable();

    #endregion
}

Notice that the factory has register and unregister functions, later on we'll see how to use them.

RegisterHandler - The register function that takes a delegate and inserts it to the hashtable with the class identifier as the key.

UnregisterHandler - The unregister function takes the type and removes whatever delegate that was there as the value.

CreateObject - The function that creates objects according to their type (key). This function extracts a delegate from the hashtable from position type and invokes the delegate (calls the static function of the object that we've registered before).

Using the factory

After comments from numerous esteemed colleagues, I changed the following code a bit, so it's more obvious that the factory doesn't "know" what type it's getting, but rather checks to see if it knows how to create an object with the provided key (type).

static void Main(string[] args)
{
    try
    {
        // registering the types that the factory will create
        ObjectFactory.RegisterHandler(Class1.ClassType, 
                new ObjectCreator(Class1.ObjectCreator));
        ObjectFactory.RegisterHandler(Class2.ClassType, 
                new ObjectCreator(Class2.ObjectCreator));
        ObjectFactory.RegisterHandler(Class3.ClassType, 
                new ObjectCreator(Class3.ObjectCreator));

        AObject aobject = null;
        // creating the objects
        for (int i = 0; i<100; i++)
        {
                aobject = ObjectFactory.CreateObject(i%3+1, null);
                aobject.Print();
        }
        // unregistering a type
        if (!ObjectFactory.UnregisterHandler(Class1.ClassType))
            Console.WriteLine("Really ?!");

        // trying to create an unregistered type
        aobject = ObjectFactory.CreateObject(Class1.ClassType, null);
        if (aobject != null)
            aobject.Print();
        else
            Console.WriteLine("aobject is null");
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
    Console.ReadLine();
}

Notice the registration process:

We pass a type to be registered and we create a new delegate passing it the static function of the type that we're registering. We have to make sure that types are registered before we can use the factory to create objects, so the registration process should be as soon as possible in the program.

One more thing to notice is that Class3 (if you've downloaded the code, you may have noticed already) belongs to the same namespace (and assembly) as Main and not to the namespace (and assembly) that the rest of the classes belong to. This means that we have achieved goal 1 and goal 2.

I leave it up to you to decide if goal 3 was achieved.

Best regards and a happy new year to all.

License

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

About the Author

Asa Meltzer
Software Developer
Israel Israel
Software designer and programmer.
Programming languages:
MFC, C++, Java , C#, VB and sometimes C and assembly.

Comments and Discussions

 
GeneralMy vote of 2 PinmemberBen_Desjardins11-Jan-10 6:44 
GeneralMisses the point of C# PinprotectorMarc Clifton11-Jan-05 14:03 
A few comments---
 
Requiring that the class you want to create with the factory be derived from AObject is not a good design. It prevents the class from deriving from anything else. An interface could be used, but even that may not be necessary--C# has the concept of an object, so why not just have the factory return that?
 
Also, I don't understand why all the layers of indirection. Since you're already passing type information, why not use Activator.CreateInstance? You can also pass a string for the type instead of the type itself and let your factory create the Type object from the string.
 
Marc
 
MyXaml
Advanced Unit Testing
YAPO
 

GeneralRe: Misses the point of C# PinmemberAsa Meltzer11-Jan-05 19:50 
GeneralRe: Misses the point of C# Pinmemberpanmanphil12-Jan-05 2:28 
GeneralDesign issue Pinmemberpanmanphil11-Jan-05 4:58 
GeneralRe: Design issue PinmemberMatt Berther11-Jan-05 6:10 
GeneralRe: Design issue PinmemberJon Rista11-Jan-05 6:11 
GeneralRe: Design issue PinmemberAsa Meltzer11-Jan-05 19:33 

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

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

| Advertise | Privacy | Mobile
Web04 | 2.8.140415.2 | Last Updated 11 Jan 2005
Article Copyright 2005 by Asa Meltzer
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid