使用Ninject.Factory而不仅仅注入IKernel有什么好处?

时间:2015-04-10 08:09:21

标签: ninject factory

根据this article(第一段),将IKernel注入任何需要的地方都是不好的做法。

相反,建议引入一个由Ninject自动实现的工厂接口(在内部执行相同的分辨率)。

这是我正在处理的实际代码:

前实施:

public class CommandServer
{
    [Inject]
    public IKernel Kernel { get; set; }

    ....

   public TResponse ExecuteCommand<TRequest, TResponse>(TRequest request)
        where TResponse : ResponseBase, new()
    {
        ...
        var command = Kernel.Get<ICommand<TRequest, TResponse>>();
        ...
    }
}

使用工厂:

public class CommandServer
{
    [Inject]
    public ICommandFactory CommandFactory { get; set; }

    ....

   public TResponse ExecuteCommand<TRequest, TResponse>(TRequest request)
        where TResponse : ResponseBase, new()
    {
        ...
        var command = CommandFactory.CreateCommand<TRequest, TResponse>();
        ...
    }
}

// at binding time:
public interface ICommandFactory
{
    ICommand<TRequest, TResponse> CreateCommand<TRequest, TResponse>();
}

Bind<ICommandFactory>().ToFactory();

我不是说我不喜欢它(它看起来很干净) - 只是不确定为什么前者特别糟糕而后者更好?

1 个答案:

答案 0 :(得分:2)

通常,您不应使用服务定位器模式。你为什么问?请参阅Mark Seeman(也有评论!)和this SO question。使用IKernel(或更好一些:只有IResolutionRoot部分)会像服务定位器一样闻起来。

现在Mark建议您应该应用Abstract Factory Pattern - 而且他还提到了动态代理方法。

我个人认为使用ninject自动生成的工厂(=动态代理方法)取而代之的是值得的。 你应该使用像:

这样的工厂
public interface IServiceLocator
{
    T Create<T>();
}

因为它的服务定位器很好; - )

然而,使用

之类的东西
public interface IResponseHandleFactory
{
    IResponseHandle Create(int responseId);
}

非常好。

当然,您也可以直接使用IResolutionRoot来代替工厂。代码看起来像:

IResolutionRoot.Get<IResponseHandle>(
    new ConstructorArgument("responseId", theResponseIdValue);

不直接使用IResolutionRoot

的原因
  • 很多IResolutionRoot&#34;方法&#34;实际上是扩展方法。这使得单元测试变得复杂(如果你想对它进行单元测试,它基本上不是一个明智的选择。)
  • 与使用工厂界面时相比,
  • 轻微与容器脱钩(=>更换DI容器的难易程度)。自动生成的工厂功能,您也可以实现作为其他容器的添加 - 如果他们已经没有(我已经为Unity和AutoFac亲自完成)。但是,它需要一些关于动态代理的技术诀窍。

工厂接口的替代方案:使用Func<>工厂。以上示例也可以由Func<int, IResponseHandle>()替换。相当多的DI容器支持这个开箱即用/标准插件(ninject需要Factory扩展)。因此,您可以更容易地与容器分离。缺点:难以进行单元测试,也没有明确命名的参数。