Factory Method Support in Castle Windsor and Spring.Net
by David Hayden
Most of the time when using Windsor I simply map a service provider to a service contract as such programmatically or in a configuration file:
<components>
<component
id="CustomerRepository"
service="Sample.ICustomerRepository, Sample"
type="Sample.CustomerRepository, Sample"
lifestyle="singleton" />
</components>
and request the service as follows:
ioc.Resolve<ICustomerRepository>();
which will then just return the type mapped to ICustomerRepository which in this case is the CustomerRepository Class. This works because I know the service provider ahead of time, CustomerRepository, and add it to the configuration file.
However, what if it isn't that easy? What if CustomerRepository won't always be the service provider for ICustomerRepository? What if the chosen service provider is based on other runtime factors that need to be taken into consideration during runtime? This is when we use the Factory Method support in Windsor and Spring.NET.
Factory Method
When service or object instantiation is
-
A little more complicated than calling new
-
Based on a number of runtime factors
-
Volatile and you want to loosely-couple creation from the caller
a Factory Method is a pretty decent choice of hiding object creation behind a method call.
Here is a defintion from Design Patterns in C#
"When you develop a class, you usually provide class constructors to let clients of you class instantiate it. There are times, though, when a client that needs an object does not or should not know which of several possible classes to instantiate. The FACTORY METHOD pattern lets a class developer define the interface for creating an object while retaining control of which class to instantiate. [Design Patterns in C#, Steven John Metsker, p. 171]"
Enterprise Library, for example, is smothered in Factory Methods, because it is based on a provider model and the actual provider chosen is determined at runtime by configuration settings in a configuration file. Here is an example from the Data Access Application Block:
Database db = DatabaseFactory.CreateDatabase();
Database is an Abstract Class and several classes, like SqlDatabase and OracleDatabase, could be returned by DatabaseFactory.CreateDatabase() depending on whether your configuration settings specify you want to connect to SQL Server or Oracle. Instantiation details are hidden behind the CreateDatabase Factory Method because the actual provider returned is based on runtime configuration settings.
Using Factory Method Support in Castle Windsor
As mentioned above, Castle Windsor will normally provide you a service provider based on the type mapped to a service contract, but you have the option of specifying a Factory Method in your code to instantiate the service provider.
Instead of mapping the CustomerRepository Class to ICustomerRepository in the Windsor Configuration, I will instead create a Factory Method on a class that returns an ICustomerRepository:
public class CustomerRepositoryFactory
{
public ICustomerRepository Create()
{
return new CustomerRepository();
}
}
Here I have just hardcoded new CustomerRepository(), but in reality I would be interrogating various runtime configuration and context settings to determine the actual provider for ICustomerRepository Services.
In my Windsor Configuration File, I can now tell Windsor to call the Create Method ( Factory Method ) on CustomerRepositoryFactory and return its results when the application requests ICustomerRepository. Notice the FactorySupport Facility which extends the container to provide Factory Method Support. Also notice that on the CustomerRepository Component we now have factoryid and factoryCreate options which specify which type and method to call to instantiate the ICustomerRepository Provider.
<facilities>
<facility
id="factory.support"
type="Castle.Facilities.FactorySupport.FactorySupportFacility, Castle.MicroKernel"
/>
</facilities>
<components>
<component
id="CustomerRepositoryFactory"
type="ConsoleApplication2.CustomerRepositoryFactory, ConsoleApplication2"
/>
<component
id="CustomerRepository"
service="ConsoleApplication2.ICustomerRepository, ConsoleApplication2"
type="ConsoleApplication2.CustomerRepository, ConsoleApplication2"
factoryId="CustomerRepositoryFactory"
factoryCreate="Create"
/>
</components>
You can also pass dependencies into the Factory Method. Let's change the CustomerRepositoryFactory Create Method to require an external service and pretend we actually use it to determine which ICustomerRepository to instantiate:
public class CustomerRepositoryFactory
{
public ICustomerRepository Create(IExternalService service)
{
return new CustomerRepository();
}
}
All we have to do is add the dependency into the Windsor Configuration File and it will provide it to the method:
<facilities>
<facility
id="factory.support"
type="Castle.Facilities.FactorySupport.FactorySupportFacility, Castle.MicroKernel"
/>
</facilities>
<components>
<component
id="ExternalService"
service="ConsoleApplication2.IExternalService, ConsoleApplication2"
type="ConsoleApplication2.ExternalService, ConsoleApplication2"
/>
<component
id="CustomerRepositoryFactory"
type="ConsoleApplication2.CustomerRepositoryFactory, ConsoleApplication2"
/>
<component
id="CustomerRepository"
service="ConsoleApplication2.ICustomerRepository, ConsoleApplication2"
type="ConsoleApplication2.CustomerRepository, ConsoleApplication2"
factoryId="CustomerRepositoryFactory"
factoryCreate="Create"
/>
</components>
Pretty slick. The use of a Factory Method does not change the interface for requesting services via Castle Windsor. The following works as expected in my sample:
WindsorContainer ioc = new WindsorContainer("../../Windsor.config");
ICustomerRepository repository = ioc.Resolve<ICustomerRepository>();
repository.Add(new Customer());
Using Factory Method Support in Spring.NET
Spring.NET supports the Factory Method as well. We just have changes in the configuration file and client code, and the CustomerRepositoryFactory stays the same.
The configuration file is as follows. Notice the factory-object and factory-method options to specify the type and factory method that will be called to instantiate the service. Also notice the constructor-arg for providing the IExternalService to the method.
<objects xmlns="http://www.springframework.net">
<object
id="ExternalService"
type="ConsoleApplication2.ExternalService, ConsoleApplication2"
/>
<object
id="CustomerRepositoryFactory"
type="ConsoleApplication2.CustomerRepositoryFactory, ConsoleApplication2"
/>
<object
id="CustomerRepository"
factory-method="Create"
factory-object="CustomerRepositoryFactory">
<constructor-arg name="service" ref="ExternalService"/>
</object>
</objects>
and the client code I used for my sample is:
IResource input = new FileSystemResource("../../Spring.config");
IObjectFactory factory = new XmlObjectFactory(input);
ICustomerRepository repository = (ICustomerRepository)factory.GetObject("CustomerRepository");
repository.Add(new Customer());
Very cool!
Conclusion
Both Caste Windsor and Spring.NET have wonderful support for Factory Methods when service instantiation needs to be delegated to a factory method in your application. For those of you using the Policy Injection Application Block, one might consider calling PolicyInjection.Wrap<ICustomerRepository>(...) in the factory method for a bit of AOP Services Enterprise Library style. This is just one option of course to get the Policy Injection Application Block to work with these containers. Of course, Windsor and Spring.NET have AOP services as well.