I'm still a newbie at DI, and I am trying to understand if I am thinking of things the wrong way. I am working on a toy problem when I want to represent a Die object that has a dependency on an IRandomProvider. That interface is simple:
public interface IRandomProvider
{
int GetRandom(int lower, int upper);
}
I want to have a Die constructor that looks like this:
Die(int numSides, IRandomProvider provider)
I'm trying to use a static DIFactory that has a method like this:
public static T Resolve<T>()
{
if (kernel == null)
{
CreateKernel();
}
return kernel.Get<T>();
}
Where CreateKernel simply binds to a specific implementation of IRandomProvider.
I want to be able to call this with:
DIFactory.Resolve<Die>(20);
I can't make this work without making a special version of "Resolve" which can allow me to deal with ConstructorArgs. That seems to make things overly complex, and would require me to modify DIFactory for every other instance of that, as well as tie to a specific name for the constructor parameter.
If I refactor the Die class to not use the int constructor, everything works fine. But now someone has to remeber to initialize the numSides parameter, which seems like a bad idea, since it is a requirement for the class.
I suspect this is a bad mental model for DI. Can anyone enlighten me?
You can skip the interface role and inject the service object directly into the client. But by doing that, you break with the dependency inversion principle and your client has an explicit dependency on the service class. In some situations, this might be ok.
Also, with constructor injection, it's easier to build immutable components. Moreover, using constructors to create object instances is more natural from the OOP standpoint. On the other hand, the main disadvantage of constructor injection is its verbosity, especially when a bean has a handful of dependencies.
Dependency Injection is done by supplying the DEPENDENCY through the class's constructor when creating the instance of that class.
There are three main styles of dependency injection, according to Fowler: Constructor Injection (also known as Type 3), Setter Injection (also known as Type 2), and Interface Injection (also known as Type 1).
An inversion of control container is not a factory. Don't use it to resolve business objects like your Die
class. Inversion Of Control is pattern used to let the container take control over the lifetime of your objects. A bonus of that is that it also supports the dependency injection pattern.
Business objects is typically created, changed and disposed. Hence no need to use the container for them. And as you just noticed, they do take their mandatory parameters in the constructor which makes it hard to use the container for them.
You can register a DieFactory
in the container and let it take the IRandomProvider
in the constructor:
public class DieFactory
{
public DieFactory(IRandomProvider provider)
{}
public Die Create(int numberOfSides)
{
return new Die(numberOfSides, _provider);
}
}
But it would of course be better to create a factory used to create all related business objects. Then you can take the kernel as a dependency:
public class AGoodNameFactory
{
public DieFactory(IKernel kernel)
{}
public Die CreateDie(int numberOfSides)
{
var provider = _kernel.Resolve<IRandomProvider>();
return new Die(numberOfSides, provider);
}
// other factories.
}
Or you could just take the IRandomProvider
as a dependency directly in the class that creates the Die
class.
You could use ConstructorArgument
together with Kernel.Get
in this particular situation.
Here is full sample.
public class ExampleKernel : NinjectModule
{
public override void Load()
{
Bind<IRandomProvider>()
.To<RandomProvider>();
Bind<Die>()
.ToSelf()
.WithConstructorArgument("numSides", 6);
// default value for numSides
}
}
var d6 = kernel.Get<Die>(); // default numSides value
var d20 = kernel.Get<Die>(new ConstructorArgument("numSides", 20)); // custom numSides
Assert.That(d6.NumSides == 6);
Assert.That(d20.NumSides == 20);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With