Pages

Saturday, 9 January 2010

Supercede Instance Pattern

This is a pattern introduced (to me at least) by Michael Feathers in his book "Working Effectively with Legacy Code". It deals with occasions where you have a Singleton pattern that it consumed by a large number of classes. This impedes these classes being Unit Tested as they all have a hidden dependency.

The first solution to this is to add a constructor overload to every class. You will also need to extract an interface for the Singleton. For instance...

public class MyClass
{
    public MyClass()
    {
    }

    public void DoWork()
    {
        MySingleton.Instance.DoWork();
    }
}

... would become ...

public class MyClass
{
    private IMySingleton _worker;

    public MyClass()
        : this (MySingleton.Instance)
    {
    }

    public MyClass(IMySingleton worker)
    {
        _worker = worker;
    }

    public void DoWork()
    {
        _worker.DoWork();
    }
}

You won't have to change any other code once the above it implemented. What this achieves is that your Unit Tests have the opportunity to use a Dependency Inversion pattern by using the non-default constructor. Simple, and nothing new.

However, this can be an awful lot of work - directly proportional to the number of classes that consume your singleton. This is where the Supercede Instance Pattern comes in.

This is probably a fair representation of the Singleton Pattern...

public class MySingleton : IMySingleton
{
    private MySingleton()
    {
    }

    private static IMySingleton _instance;

    public static IMySingleton Instance
    {
        get
        {
            // Double locking mechanism omitted for brevity...
            if (_instance == null)
                _instance = new MySingleton();
        }
    }
}

All we need to do is add a new method to the above class...

public class MySingleton : IMySingleton
{
    // All previous members omitted for brevity

    public static void SupercedeInstance(IMySingleton newInstance)
    {
        _instance = newInstance;
    }
}

Et voila. Your unit tests can call this new method and inject their own, chosen object into the Singleton. You've now broken the dependency of masses of classes in one hit. Nice.

Final point, if your language allows you could conditionally remove this method. That would keep your singleton 'safe' in the wild. For example...

public class MySingleton : IMySingleton
{
    #if RUNNING_UNIT_TESTS

    public static void SupercedeInstance(IMySingleton newInstance)
    {
        _instance = instance;
    }

    #endif
}

No comments:

Post a Comment