如何解耦IoC框架实现

15
我一直在学习IoC、依赖注入等技术,并且很喜欢这个过程。对我来说,解耦和基于接口编程的好处是显而易见的。
然而,我真的不喜欢绑定到特定的框架,比如Unity或Autofac或Windsor - 因为我还在学习中,还没有决定哪种最适合我的目的。
那么,我该如何包装Unity之类的东西,以便稍后可以轻松地切换到Windsor(或其他)?并且你别告诉我用另一个来注入第一个。
谢谢!
R.
P.s. 我标记了Unity,因为那是我目前的个人偏好(我只是喜欢Entlib)。
5个回答

19
你可以尝试通过声明一个带有ResolveRegisterIContainer来从容器中抽象出内容。我以前试过几次。然后,你可以实现一个Container : IContainer并用你的抽象封装一个实际的IoC容器。我曾试过使用Unity和Castle Windsor。

但是,很快我意识到这真的是一种过度工程。我明白了我试图从抽象中抽象出来,却建立了另一个抽象。这可能对于学习概念来说很好,但在一个真正的项目中,这会让人非常头疼。我强烈建议不要从IoC容器中进行抽象。如果你正确地使用DI原则,改变你的容器将是相当容易的。

代码看起来过于复杂,像:

//I did this mess with Service Locator
var t = ContainerService.Instance.Resolve<IMyType>();
//others could go further with same Service Locator
var t = IoCFactory.Instance.CurrentContainer.Resolve<IMyType>();

//better way, use --> IoC and DI <--
//when a program starts, or a new instance of the context created
var t = Container.Resolve<IMyType>() //this lives at the bottom of the stack
//and then you just pass IMyType to the constructor of other types    
//you don't need to call Resolve again in the logical cycle

请参考Ayende的这篇文章

是的,他们抽象化了控制反转容器。我认为,如果您需要这样做,那么很明显您并不真正理解IoC的全部含义。


你说得很对,这是为了学习需求。一旦我选择了我的容器,我会很高兴的。但是当你在学习并且不得不重写应用程序以适应另一个容器,因为博客上的示例使用x而不是y时,这会让你感到疲惫。我觉得拥有自己的示例并找出一种轻松更换博客所使用的任何容器来说明特定概念的方法会很好。 - Richard
我同意,这有点过头了。一个好的IoC容器应该有足够的配置空间,这样你就不必在代码中引用容器。如果抽象容器是使其工作的唯一方法,那么我会说你正在使用一个糟糕的IoC容器。 - FMM
4
@Richard,重点是即使在最复杂的项目中,只应有少数几个类主动引用容器。其它所有类都应该注入其依赖项。因此抽象化是没有意义的。 - Paul Stovell

18
使用构造函数注入来表明一个类需要哪些依赖项。您列出的每个容器都支持它。
有时,一段代码无法实现完全的容器独立性,但这种情况应该是代码库的很小一部分。

10

DI(依赖注入)容器应该只被从组合根引用,其他模块不应该引用容器。

-Mark Seemann (.NET 依赖注入 作者)

换句话说,如果你更改了 DI 容器,那么你只需要更改一个类。

通常情况下,构造函数注入是正确的方式,正如其他人所提到的。你可以通过注入工厂接口或者 Func<T> 委托来实现动态创建对象。

我建议尽可能避免使用 XML 配置。


4

正如其他人提到的那样,最好使用构造函数注入。这将解决你的许多问题。

如果你的类直接依赖于IoC容器本身,它往往是使用服务定位器(反)模式的一种变体。在这种情况下,隔离使用服务定位器解析的类型,并使用工厂接口抽象该动态解析。例如,替换为:

public class Foo
{
    private MyIoCContainer _container;

    public Foo(MyIoCContainer container)
    {
        this._container = container;
    }


    public void DoSomething()
    {
        // have to do this at runtime for whatever reason
        var myObj = this._container.Resolve<ISomeType>();

        myObj.DoSomething();
        myObj.DoSomethingElse();
    }
}

用这个:
public class Foo
{
    private IObjFactory _provider;

    public Foo(IObjFactory _provider)
    {
        this._provider = provider;
    }


    public void DoSomething()
    {
        var myObj = _provider.GetObj();

        myObj.DoSomething();
        myObj.DoSomethingElse();
    }
}

public interface IObjFactory
{
    ISomeType GetObj();
}

现在,你有一个IObjFactory,它可以封装构造实现ISomeType的对象的动态运行时特性。如果你从容器/服务定位器中构造许多不同类型的对象,则应该至少拥有与之相对应的*Factory接口(符合接口隔离原则)。

3

6
在我看来,仅当构建一个组件并在多个不同的项目中使用时,CSL 才真正有用。这些项目本身可能选择使用不同的 IoC 库。例如,在自己的单一业务应用程序中使用 CSL 是过度的。它增加了很多依赖管理,并且提供的接口非常狭窄,可以在应用程序的核心基础设施库中特定地简单实现。 - quentin-starin
3
作为 CSL 的其中一位设计者,我坚定地支持上述评论。CSL 旨在为想要容器的库作者提供选择,但不希望强制库的容器选项对应用程序造成影响。 - Chris Tavares

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接