A Lazy Initializing Strategy Selector in .NET 4

Before I start, I should point out that I would never use the following code in production. I think it’s overkill. In a situation where I wanted to map some real-world key to a strategy, say a database value to the code which handles that option, I’d probably write code a bit like this:

// store a one-time lookup table
private static Dictionary<string, IMyStrategy> lookup = new Dictionary<string, IMyStrategy>();

// then in some static initializer somewhere
lookup.Add("thing1", new Strategy1());
lookup.Add("myThing", new StrategyForMyThing());
.
.
.

etc

// then in code to get the strategy
if (lookup.ContainsKey(myValue)) 
{
   // do something with strategy
   Proceed(lookup[myValue]);
}

This is simple. The alternative, and there are cases for an against this. is a big switch statement to do the same thing.

However, a reader request is for a lazy initializing generic strategy selector. How would you write such a thing. The Coding Craftsman would do it with a minimum of code, and a lot of library reuse. He’d also use TDD.

To get a feel for this code, let’s look at the tests:

    [TestFixture]
    public class StrategySelectorTest
    {
        private static StrategySelector<Options, IStrategy> selector;

        static StrategySelectorTest()
        {
            selector = new StrategySelector<Options,IStrategy>();
            selector.Register(Options.Blue, typeof (BlueStrategy));
            selector.Register(Options.Red, typeof (RedStrategy));
        }

        [Test]
        public void Red()
        {
            Assert.That(selector[Options.Red].GetResult(), Is.EqualTo("Red"));
        }

        [Test]
        public void Blue()
        {
            Assert.That(selector[Options.Blue].GetResult(), Is.EqualTo("Blue"));
        }
    }

The key elements are a “Register” pattern during initialization and then the use of the [] operator to select the strategy. In this example, we’re keying using an Enum:

    enum Options
    {
        Blue,
        Red
    }

The interface that the strategy has, and the simple implementations of that to prove the pattern is as follows:

    interface IStrategy
    {
        string GetResult();
    }

    public class BlueStrategy : IStrategy
    {
        public string GetResult()
        {
            return "Blue";
        }
    }

    public class RedStrategy : IStrategy
    {
        public string GetResult()
        {
            return "Red";
        }
    }

So we have the chess pieces in place. What’s the magic behind the strategy selector? The brief here was to make a strategy selector which did not construct its strategy objects until needed. So we’re using the Lazy class from .NET 4.

    public class StrategySelector<KEY, T> where T : class
    {
        private Dictionary<KEY, Lazy<T>> lookupTable = new Dictionary<KEY, Lazy<T>>();


        public void Register(KEY key, Type strategyType)
        {
            if (lookupTable.ContainsKey(key))
            {
                throw new ApplicationException("Double registration of " + key.ToString());
            }

            // add lazy initializing instance of strategy
            lookupTable.Add(key, new Lazy<T>(() => ConstructFrom(strategyType)));
        }

        // accessor
        public T this[KEY red]
        {
            get { return lookupTable[red].Value; }
        }

        /// <summary>
        /// Invoke the default constructor - used by the lazy initializor
        /// </summary>
        /// <param name="t">type</param>
        /// <returns>new object, case as our strategy type</returns>
        private T ConstructFrom(Type t)
        {
            ConstructorInfo ci = t.GetConstructor(Type.EmptyTypes);
            return ci.Invoke(new object[] {}) as T;
        }
    }

This works:

Conclusion
I cannot see a good reason to use this for the majority of strategy scenarios. As it uses the Lazy object to wrap the initialization of the strategies, and since a strategy archive like this tends towards containing an instance of every strategy by the end of the program’s lifetime, the overhead of Lazy objects (though small) is >0 and so is not a benefit. It adds complexity.

On top of that, most strategy objects have no major initialization effort, so should probably be instantiated up front.

Having said that, this is an interesting exercise and some of the syntax and ideas may be useful in other situations, or for things which are not strictly the strategy pattern.

Advertisements

2 comments

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s