我仍然是DI的新手,我想知道我是否错误地思考问题。当我想表示一个依赖于IRandomProvider的Die对象时,我正在解决玩具问题。那个界面很简单:
public interface IRandomProvider
{
int GetRandom(int lower, int upper);
}
我想要一个看起来像这样的Die构造函数:
Die(int numSides, IRandomProvider provider)
我正在尝试使用具有如下方法的静态DIFactory:
public static T Resolve<T>()
{
if (kernel == null)
{
CreateKernel();
}
return kernel.Get<T>();
}
CreateKernel只是绑定到IRandomProvider的特定实现。
我希望能够通过以下方式调用此方法:
DIFactory.Resolve<Die>(20);
如果不制作特殊版本的“Resolve”,我就无法完成这项工作,这可以让我处理ConstructorArgs。这似乎使事情变得过于复杂,并且需要我为其他每个实例修改DIFactory,以及绑定到构造函数参数的特定名称。
如果我重构Die类不使用int构造函数,一切正常。但现在有人必须记住初始化numSides参数,这似乎是一个坏主意,因为这是该类的要求。
我怀疑这对DI来说是一个糟糕的心理模型。谁能开导我?
答案 0 :(得分:9)
控制容器的倒置不是工厂。不要使用它来解析像Die
类这样的业务对象。 Inversion Of Control是用于让容器控制对象生命周期的模式。这样做的一个好处是它也支持依赖注入模式。
通常会创建,更改和处置业务对象。因此无需使用容器。正如您刚才注意到的那样,它们确实在构造函数中使用了它们的强制参数,这使得容器很难用于它们。
您可以在容器中注册DieFactory
,然后让它在构造函数中使用IRandomProvider
:
public class DieFactory
{
public DieFactory(IRandomProvider provider)
{}
public Die Create(int numberOfSides)
{
return new Die(numberOfSides, _provider);
}
}
但是创建一个用于创建所有相关业务对象的工厂当然会更好。然后你可以将内核作为依赖项:
public class AGoodNameFactory
{
public DieFactory(IKernel kernel)
{}
public Die CreateDie(int numberOfSides)
{
var provider = _kernel.Resolve<IRandomProvider>();
return new Die(numberOfSides, provider);
}
// other factories.
}
或者您可以直接在创建IRandomProvider
类的类中将Die
作为依赖项。
答案 1 :(得分:5)
在这种特殊情况下,您可以将ConstructorArgument
与Kernel.Get
一起使用。
这是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);