使用IoC高级功能和自己的抽象

时间:2011-08-29 09:22:27

标签: .net inversion-of-control ninject

我已经阅读了许多有关在开发中使用IoC容器的有趣文章。在许多情况下,作者建议我们编写自己的简单包装器,它隐藏了读取容器的接口,如Ninject,Castle.Windsor,Unity等。

包装器的实现看起来像这样(对于Ninject):

/// <summary>
/// Contains links for service contract and realization.
/// </summary>
public static class IoC
{
    static readonly IKernel kernel;
    static IoC()
    {
        kernel = new StandardKernel();
    }

    public static void RegisterService<T>(Type service)
    {
        kernel.Bind<T>().To(service);
    }

    public static T Resolve<T>()
    {
        return kernel.Get<T>();
    }
}

好的,但我如何使用此模式的高级功能?例如,我喜欢使用像InRequestScope()或InSingletonScope()这样的生命周期管理修饰符,许多容器都支持这种修饰符。使用上面的模式,我可以扔掉ninject并使用15-lines implementation。区别在哪里?

我还看了CommonServiceLocator试图找到丰富的包装器,但它与我的IoC类没有太大区别。 现在我认为我不应该制作自己的自行车并直接使用全功能容器。你呢?

3 个答案:

答案 0 :(得分:6)

您的应用程序代码应完全忽略DI容器的存在,无论哪一个。相反,需要依赖的类应该通过构造函数注入来请求它们,如下所示:

public class Foo
{
    private readonly IBar bar;

    public Foo(IBar bar)
    {
        if (bar == null)
            throw new ArgumentNullException("bar");

        this.bar = bar;
    }

    public void SomeMethod()
    {
        // use this.bar from here...
    }
}

注意Guard子句和readonly关键字的组合如何保护类的不变量。从Foo类的任何方法,this.Bar都可以保证可用。

所有DI容器都了解这种设计模式,因此您可以让您选择的容器构成Composition Root的整个应用程序图。从这里,您可以使用所有容器的高级功能,而无需担心代码库的其余部分与容器耦合。

答案 1 :(得分:1)

做一些死灵法术,但我认为其他答案错过了你的观点 - 部分。 您一直在谈论&#34;高级功能&#34;喜欢范围。虽然实际实现没有引用容器,但它仍然在某种程度上依赖于这些功能。所以我同意,即使容器仅用于组合根目录,您的实现可能不会是100%容器不可知。如果是的话,那就意味着你在某些方面重新发明轮子(比如请求范围......)

现在,如果您只需要&#34;标准&#34;像SingletonRequest这样的范围 - 由许多容器支持 - 使用抽象(自定义)可能是值得的,因此您可以轻松地换出容器(例如,您可能想要基准测试它们,...)。

当您开始接近更多高级功能时,例如自定义范围,Ninject .InCallScope().InNamedScope()它越来越有可能交换容器的工作量太大。因此,&#34;抽象&#34;几乎没用,因为&#34;抽象&#34;非常特定于容器。

现在你应该彻底调查是否&#34;锁定&#34;进入特定的DI容器是值得的高级功能的好处。

您使用的专用容器功能越多,切换容器的工作就越多,可能会使得它非常经济。它限制了您未来的选择。如果没有更新......你就会陷入困境。如果它对于不断增长的用户群来说太慢了......你就会陷入困境。

您必须平衡独立性,可测试性,可维护性,简单性(......以及更多标准)。

我不认为有人可以在不知道你的具体情况的情况下给你一个有效的答案。此外,这方面的经验往往取决于自己的经验。当一个人开发框架时,可能会说'#34;永远不会&#34;。如果有人在开发中等规模的最终用户应用程序,可能会说,这是值得的。

我个人一直在使用高级功能,如命名范围,调用范围,自定义范围,ninject.extensions.Factory(甚至更强大的自定义版本),拦截,上下文保留,上下文操作,条件绑定,... 很多。在整整4年的开发过程中(8位开发人员),没有人对此表示遗憾。只是说人们可以满意。但我仍然建议每个人都要非常小心这个决定。我当然不会提倡它。

最后一件事。我所知道的最可怕的项目是一个,试图做一切完美,推迟多次发布,并以大量的花钱而不是一分钱收入结束。创建一个糟糕的软件,赚钱,然后必须修复大量的技术债务是不好的。但与一开始就不赚一分钱相比,这是一个奢侈的问题。

答案 2 :(得分:0)

我不建议抽象你的IOC框架。对于95%的应用程序,没有充分的理由可以满足您的需求。

只需公开这样的内核:

public static class IoC
{
     public IKernel Kernel { get { return _kernel; } }

     ... etc ...
}

如果您迫切希望抽象出您的IOC框架,那么我仍然可以在需要时直接访问真实框架。例如,您可以将抽象的内核强制转换为实际的内核。当然,当你想要使用抽象不提供的特定功能时,你偶尔会这样做。

关于你从NInject发布的例子有一点要注意 - 我会说这个静态IOC类的目的是提供对内核的单例实例的访问,而不是从应用程序中隐藏NInject。