When dependency injection meets IDisposable
What follows is a solution which should really have been obvious to me the first time I faced the problem: how do you decouple logic and instantiation of collaborating objects when faced with a class you need to instantiate inline, such as an IDisposable class? Consider this:
public class NiceClass { private DependentClass injectedDependency; public NiceClass(DependentClass injectedDependency) { this.injectedDependency = injectedDependency; } public void CoupledMethod() { using (IFace c = new Face(injectedDependency)) { // do something with c } } } |
We can see that injectedDependency is not coupled, but what about the local instance of Face in CoupledMethod? To decouple it use a delegate to return the IFace instance instead of calling new directly. If the coupling had been to a concrete type (Face), we could refactor to an abstract type (IFace) or wrapper first.
public class NiceClass { private DependentClass injectedDependency; private Func<DependentClass, IFace> faceFactoryMethod; public NiceClass( DependentClass injectedDependency, Func<DependentClass, IFace> faceFactoryMethod) { this.injectedDependency = injectedDependency; this.faceFactoryMethod = faceFactoryMethod; } public void CoupledMethod() { using (IFace c = this.faceFactoryMethod(injectedDependency)) { // do something with c } } } |
This is an additional three lines of code, which arguably makes the control flow harder to read statically. It does, however, improve testability. I think of this as equivalent to the classic tradeoff between abstract and concrete parameters.
Instantiating NiceClass is not too arduous either with a lambda expression (although it is still a little verbose).