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):
The results... |
Summary
This was just a quick brain dump to illustrate that the TypedFactoryFacility can do generics as well.