Geeklog
There's no place like 127.0.0.1 - ramblings about programming

Advanced Object Builder - Singleton Policy

April 17, 2008 14:30 by The Geek

This a continuation from here, here, here and here.

The singleton pattern can be very useful in many situations.  In this post I will describe how we can add a simple implementation to our object builder.

The first thing we need is a way of determining which classes should be treated as a singleton.  We could use a custom attribute, a configuration section, or the method that I have chosen, an interface:

    public interface ISingleton { }

For the purposes of this example, I have created an interface that we will ask the object builder for:

    public interface IAnimalList
        : IList<string>
        , ISingleton
    {
    }

This means that we will also need an implementation:

    public class AnimalList
        : List<string>
        , IAnimalList
    {
        public AnimalList()
        {
            this.Add("Dog");
            this.Add("Cat");
            this.Add("Mouse");
            Console.WriteLine("-=AnimalList Init=-");
        }
    }

Let's change our consuming program to look like this:

    static void Main(string[] args)
    {
        BuildAndWriteList();
        BuildAndWriteList();

        Console.ReadLine();
    }

    static void BuildAndWriteList()
    {
        IAnimalList lst = Builder.Build<IAnimalList>();

        Console.WriteLine();
        Console.WriteLine("Writing list...");

        foreach (string name in lst)
        {
            Console.WriteLine("Animal: {0}.", name);
        }
    }

So that the object builder knows what to do, we need to add a type substitution item to the app.config file:

    <add originalType="ObjectBuilder_Singleton.IAnimalList"
         replacementType="ObjectBuilder_Singleton.AnimalList"
         replacementAssembly="ObjectBuilder_Singleton" />

Now if we run the program, we will get the following output:

As you can see, the AnimalList is being constructed twice.  How can we alter the behaivour of the Builder class so that the second call to the object builder returns the first instance?

Well as this is a simple example, we will use a simple approach.  What we will do is maintain a list of singleton objects:

    private static Dictionary<Type, object> _singletons = new Dictionary<Type, object>();

Now we can refactor the Builder.Build<T> method to ensure that there is only ever one item of each singleton:

    public static T Build<T>(params object[] args)
    {
        //get the type that we are going to build
        Type toBuild = typeof(T);

        //should we treat it as a singleton?
        Type isSingleton = toBuild.GetInterface(typeof(ISingleton).Name);

        if (isSingleton != null)
        {
            if ((args == null) || (args.Length == 0))
            {
                //do we already have an instance?
                if (!_singletons.ContainsKey(toBuild))
                {
                    //no instance, build it
                    T instance = InstanceManufacturer<T>.CreateInstance(args);

                    //and store it for future use
                    _singletons.Add(toBuild, instance);
                }
                //return the cached instance
                return (T)_singletons[toBuild];
            }
            else
            {
                throw new NotImplementedException(Resources.SingletonCantHaveArgs);
            }
        }
        else
        {
            //just build it
            return InstanceManufacturer<T>.CreateInstance(args);
        }
    }

As you can see above, we check if the type is a singleton (by checking if it implements ISingleton).  If it is, we add it to our dictionary (if it does not exist) and then return the object from the dictionary.  Now when we run the program, we get the following output:

Now you can see that the list is only being constructed once (it is missing the second "-=AnimalList Init=-" line).

This method works fine for simple applications but please bear the following items in mind:

  1. The method will only work if objects are always created with the object builder (there is nothing to stop a consumer from directly creating multiple instances of AnimalList).
  2. The objects are never removed from the dictionary (so this would be unsuitable for large objects / many objects).

As always, have fun.  In my next post I will write about how we can add dependency injection features to the object builder.

Source code: ObjectBuilder_Singleton.zip (86.14 kb)


Tags: ,
Categories: C#
Actions: E-mail | Permalink | Comments (0) | Comment RSSRSS comment feed

Comments