使用非接口的构造函数参数的dependency injection

我仍然是DI的新手,而我正在努力理解我是否以错误的方式思考问题。 当我想表示一个依赖于IRandomProvider的Die对象时,我正在处理一个玩具问题。 那个界面很简单:

public interface IRandomProvider { int GetRandom(int lower, int upper); } 

我想要一个看起来像这样的Die构造函数:

 Die(int numSides, IRandomProvider provider) 

我正在尝试使用具有如下方法的静态DIFactory:

  public static T Resolve() { if (kernel == null) { CreateKernel(); } return kernel.Get(); } 

CreateKernel只是绑定到IRandomProvider的特定实现。

我希望能够通过以下方式调用此方法:

 DIFactory.Resolve(20); 

如果没有制作特殊版本的“Resolve”,我就无法完成这项工作,这可以让我处理ConstructorArgs。 这似乎使事情变得过于复杂,并且需要我为其他每个实例修改DIFactory,以及绑定到构造函数参数的特定名称。

如果我重构Die类不使用int构造函数,一切正常。 但现在有人必须记住初始化numSides参数,这似乎是一个坏主意,因为它是该类的要求。

我怀疑这对DI来说是一个糟糕的心理模型。 任何人都可以开导我吗?

控制容器的倒置不是工厂。 不要用它来解决像Die类这样的业务对象。 Inversion Of Control是一种模式,用于让容器控制对象的生命周期。 奖励是它还支持dependency injection模式。

通常会创建,更改和处置业务对象。 因此无需使用容器。 正如您刚刚注意到的那样,它们确实在构造函数中使用了它们的强制参数,这使得容器很难用于它们。

您可以在容器中注册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(); return new Die(numberOfSides, provider); } // other factories. } 

或者您可以直接在创建Die类的类IRandomProvider作为依赖项。

在这种特殊情况下,您可以将ConstructorArgumentKernel.Get一起使用。 这是完整的样本 。

模块配置

 public class ExampleKernel : NinjectModule { public override void Load() { Bind() .To(); Bind() .ToSelf() .WithConstructorArgument("numSides", 6); // default value for numSides } } 

解析代码

 var d6 = kernel.Get(); // default numSides value var d20 = kernel.Get(new ConstructorArgument("numSides", 20)); // custom numSides Assert.That(d6.NumSides == 6); Assert.That(d20.NumSides == 20);