I got confused in implementing Injection Dependency in one specific example.
Let's say we have a class called SomeClass, which has a dependency on the type of IClassX.
public class SomeClass { public SomeClass(IClassX dependency){...} }
The creation of specific implementations of the IClassX interface depends on the run-time parameter.
With this constructor, I canβt configure the DI container (used by Unity) because I donβt know which implementation of IClassX will be used at runtime. Mark Semann, in his book, βInjecting Dependencies In .Net,β suggests using Abstract Factory as an injection parameter.
Now we have SomeAbstractFactory, which returns an implementation of IClassX based on runtime paramater runTimeParam.
public class SomeAbstractFactory { public SomeAbstractFactory(){ } public IClassX GetStrategyFor(int runTimeParam) { switch(runTimeParam) { case 1: return new ClassX1(); case 2: return new ClassX2(); default : return new ClassDefault(); } } }
SomeClass now accepts ISomeAbstractFactory as the injection parameter:
public class SomeClass { public SomeClass(ISomeAbstractFactory someAbstractfactory){...} }
And that is wonderful. We have only one root structure, where we create a graph of objects. We configure the Unity container to enter SomeAbstractFactory in SomeClass.
But suppose the classes ClassX1 and ClassX2 have their own dependencies:
public class ClassX1 : IClassX { public ClassX1(IClassA, IClassB) {...} } public class ClassX2 : IClassX { public ClassX2(IClassA, IClassC, IClassD) {...} }
How to resolve dependencies of IClassA, IClassB, IClassC, and IClassD?
1. Injection through the SomeAbstractFactory constructor
We can introduce specific implementations of IClassA, IClassB, IClassC, and IClassD in SomeAbstractFactory as follows:
public class SomeAbstractFactory { public SomeAbstractFactory(IClassA classA, IClassB classB, IClassC classC, IClassD classD) {...} ... }
The Unity container will be used in the original root of the composition, and then use poor mans DI to return a specific ClassX1 or ClassX2 class based on the runTimeParam parameter
public class SomeAbstractFactory { public SomeAbstractFactory(IClassA classA, IClassB classB, IClassC classC, IClassD classD){...} public IClassX GetStrategyFor(int runTimeParam) { switch(runTimeParam) { case 1: return new ClassX1(classA, classB); case 2: return new ClassX2(classA, classC, classD); default : return new ClassDefault(); } } }
Problems with this approach:
- SomeAbstractFactory is aware of dependencies that do not actually belong to it.
- For a deeper graph of objects, you need to change the SomeAbstractFactory constructor and class implementation
- DI container will not be used to resolve dependencies, poor DI should be used.
2. Explicit call of the DI container
Instead of the newbie of class X1 or ClassX2, we will enable them using the DI container.
public class SomeAbstractFactory { public SomeAbstractFactory(IUnityContainer container){...} public IClassX GetStrategyFor(int runTimeParam) { switch(runTimeParam) { case 1: return container.Resolve<IClassX>("x1"); case 2: return container.Resolve<IClassX>("x2"); default : return container.Resolve<IClassX>("xdefault"); } } }
Problems with this approach:
- DI container passed to SomeAbstractFactory
- The DI Resolve method is not used only in the root of the composition (ServiceLocator anti-pattern)
Is there an even more suitable approach?