DI Framework Challenges, 1: Simple Factories
June 23rd, 2008
I am not sure what is the exact name for this pattern (probably Factory Method), but I often have a situation where I have to select a specific derived type based on some parameters.
For example, I may have a SyncFactory with CreateSync method that creates a Sync based on number of parameters.
public class SyncFactory { public ISync CreateSync(…) { if (…) return new FullSync(…); if (…) return new ExportSync(…); return new SimpleSync(…); } }
This is a simplest situation. Now a realistic case may require synÑ operations to depend on some services.
Ok, let’s put them into a DI container and resolve them:
public class SyncFactory { // IResolver abstracts specific DI implementation // and the fact we are using DI. It is also mockable. public SyncFactory(IResolver<ISync> resolver) { … } public ISync CreateSync(…) { if (…) return resolver.Resolve<FullSync>(); if (…) return resolver.Resolve<ExportSync>(); return resolver.Resolve<SimpleSync>(); } }
Up to now everything went quite smooth. But what if the factory needs to give some additional parameters to the syncs, like some SyncEndPoint?
With this in mind, I immediately stumble into two problems. First one is practical: how to pass parameters to the component being resolved? Second one is perfectionist: if components make no sense without these additional parameters, why should they be registered and available in a DI container?
I have been working with Castle IoC framework and I have found no good solution to both of these problems. First one can be solved by passing “additionalArguments” to Resolve methods, but this couples me to parameter names.
What I would like to do is to have some kind of local scope so I could do the following:
public class SyncFactory { public ISync CreateSync(…) { using (var endPoint = this.CreateEndPoint(…)) using (var scope = new ComponentScope(endPoint)) { if (…) return resolver.Resolve<FullSync>(); if (…) return resolver.Resolve<ExportSync>(); return resolver.Resolve<SimpleSync>(); } } }
For second one, it makes sense to have a Create<T>() method on the container that does not require T to be registered, but does the same constructor discovery/creation as it does for registered components. Otherwise, we have to register useless components in the container, which leads to container pollution.
Unfortunately, Windsor/MicroKernel does not have this feature.
I am still not sure whether it’s me doing something the wrong way or argument coupling and container pollution are considered not that big of a deal. For me they seem to be a good feature test for DI Framework, and I am yet to see one with these fixed.