Tuesday, April 24, 2012

IoC: Windsor TypedFactoryFacility and Generics

Preface

Castle.Windsor is a powerful IoC framework. Since being cajoled into using it by a co-worker, I've come to really like it's features. Today I'm making a brain dump of something cool the TypedFactoryFacility can do. For those that don't know, this facility provides, "... automatically generated abstract factories..."

The Problem

I was creating a service host which exposed a few endpoints. These in turn called the business specific functionality. The service class already had four or five business specific methods on it. That's a lot of dependencies to put in a constructor.

    public class DrinkServiceWithoutFactory : IDrinkService
    {
        private readonly Fridge fridge;
        private readonly Machine machine;

        public DrinkServiceWithoutFactory(Fridge fridge, Machine machine)
        {
            this.fridge = fridge;
            this.machine = machine;
        }

        public IList<Can> GetCans()
        {
            return fridge.Dispense();
        }

        public IList<Bottle> GetBottles()
        {
            return machine.Dispense();
        }
    }

    public class Fridge : IDispenser<Can>
    {
        public IList<Can> Dispense()
        {
            Console.WriteLine("Creating 1 Can...");

            return new List<Can>
                   {
                       default(Can)
                   };
        }
    }

    public class Machine : IDispenser<Bottle>
    {
        public IList<Bottle> Dispense()
        {
            Console.WriteLine("Creating 2 bottles...");

            return new List<Bottle>
                   {
                       default(Bottle),
                       default(Bottle)
                   };
        }
    }

The Solution

An abstract factory was used to create the business-specific objects on demand, reducing the number of dependencies being injected into the constructor. the TypedFactoryFacility provides a handy means of creating delegate- or interface- based abstract factories automagically. What's not illustrated on the Castle documents site is that it can also create factories with generic methods.

The interface-based factory docs page explains how to create and use a factory interface. It doesn't really mention that the create method can also contain a generic. Below is an example of one that does.

    public interface IDispenserFactory
    {
        T Create<T>()
            where T : IDispenser;

        void Release(IDispenser iDispenser);
    }


This depends on two other definitions:

    public interface IDispenser
    {
        
    }

    public interface IDispenser<T> : IDispenser
        where T : class 
    {
        IList<T> Dispense();
    }
Now that we have the factory setup, we can register the components:
    public class Program
    {
        static void Main()
        {
            try
            {
                var container = new WindsorContainer();

                container.AddFacility<TypedFactoryFacility>();

                container.Register(
                    Component.For<Demo>(),
                    Component.For<Machine>(),
                    Component.For<Fridge>(),
                    Component.For<IDispenserFactory>().AsFactory(),
                    Component.For<IDrinkService>().ImplementedBy<DrinkService>()
                    );

                var demo = container.Resolve<Demo>();
                demo.Run();
            }
            catch (Exception exception)
            {
                Console.WriteLine(exception);
            }   
            finally
            {
                Console.ReadKey();
            }
        }
    }
    public class Demo
    {
        private readonly IDrinkService service;

        public Demo(IDrinkService service)
        {
            this.service = service;
        }

        public void Run()
        {
            var bottles = service.GetBottles();
            Console.WriteLine("Bottles created: {0}", bottles.Count);

            var cans = service.GetCans();
            Console.WriteLine("Cans created: {0}", cans.Count);
        } 
    }

We can then change the service implementation to use the abstract factory:

    public class DrinkService : IDrinkService
    {
        private readonly IDispenserFactory factory;

        public DrinkService(IDispenserFactory factory)
        {
            this.factory = factory;
        }

        public IList<Can> GetCans()
        {
            var fridge = factory.Create<Fridge>();
            return fridge.Dispense();
        }

        public IList<Bottle> GetBottles()
        {
            var machine = factory.Create<Machine>();
            return machine.Dispense();
        }
    }

The results (quick and dirty):

Console output of the different classes.
The results...

Summary

This was just a quick brain dump to illustrate that the TypedFactoryFacility can do generics as well.