使用非接口的构造函数参数的依赖注入

Ale*_*ick 9 c# dependency-injection ninject

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

public interface IRandomProvider 
{
   int GetRandom(int lower, int upper);
}
Run Code Online (Sandbox Code Playgroud)

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

Die(int numSides, IRandomProvider provider)
Run Code Online (Sandbox Code Playgroud)

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

    public static T Resolve<T>()
    {
        if (kernel == null)
        {
            CreateKernel();
        }
        return kernel.Get<T>();
    }
Run Code Online (Sandbox Code Playgroud)

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

我希望能够用以下方式调用:

DIFactory.Resolve<Die>(20);
Run Code Online (Sandbox Code Playgroud)

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

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

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

jga*_*fin 9

控制容器的反转不是工厂.不要用它来解析像你的Die类这样的业务对象.Inversion Of Control是用于让容器控制对象生命周期的模式.奖励是它还支持依赖注入模式.

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

您可以DieFactory在容器中注册a 并让它IRandomProvider在构造函数中使用:

public class DieFactory
{
    public DieFactory(IRandomProvider provider)
    {}

    public Die Create(int numberOfSides)
    {
        return new Die(numberOfSides, _provider);
    }
}
Run Code Online (Sandbox Code Playgroud)

但是创建一个用于创建所有相关业务对象的工厂当然会更好.然后你可以把内核作为依赖:

public class AGoodNameFactory
{
    public DieFactory(IKernel kernel)
    {}

    public Die CreateDie(int numberOfSides)
    {
        var provider = _kernel.Resolve<IRandomProvider>();
        return new Die(numberOfSides, provider);
    }

    // other factories.
}
Run Code Online (Sandbox Code Playgroud)

或者你可以IRandomProvider直接在创建Die类的类中作为依赖项.


Aki*_*kim 5

你可以在这种特殊情况下ConstructorArgument一起使用Kernel.Get.这是完整的样本.

模块配置

public class ExampleKernel : NinjectModule
{
    public override void Load()
    {
        Bind<IRandomProvider>()
            .To<RandomProvider>();

        Bind<Die>()
            .ToSelf()
            .WithConstructorArgument("numSides", 6);
                           //  default value for numSides
    }
}
Run Code Online (Sandbox Code Playgroud)

解析代码

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);
Run Code Online (Sandbox Code Playgroud)