依赖倒置。对象创建

时间:2011-08-26 11:23:33

标签: c# dependency-injection solid-principles

根据SOLID原则,类不能依赖于其他类,必须注入依赖项。这很简单:

class Foo
{
    public Foo(IBar bar)
    {
        this.bar = bar;
    }

    private IBar bar;
}

interface IBar 
{
}

class Bar: IBar 
{
}

但是,如果我希望我的Foo类能够创建Bar,而不知道IBar背后的确切实现呢? 我可以在这里考虑4个解决方案,但所有这些解决方案似乎都有缺点:

  1. 注入对象类型并使用反射
  2. 使用Generics
  3. 使用“服务定位器”并调用Resolve()方法。
  4. 创建一个单独的工厂类并将其注入Foo:

  5. class Foo
    {
        public void DoSmth(IBarCreator barCreator) 
        {
            var newBar = barCreator.CreateBar();
        }
    }
    
    interface IBarCreator 
    {
        IBar CreateBar();
    }
    
    class BarCreator : IBarCreator
    {
        public IBar CreateBar()
        {
            return new Bar();
        }
    }
    

    最后一种情况看起来很自然,但BarCreator类的代码太少了。 那么你怎么看,哪个最好?

5 个答案:

答案 0 :(得分:3)

在这种情况下,我喜欢“注入”Func<IBar>。像这样:

class Foo
{
    public Foo(Func<IBar> barCreator)
    {
        this.bar = barCreator();
    }

    private IBar bar;
}

interface IBar 
{
}

答案 1 :(得分:3)

这就是制造工厂的原因。

如果您觉得您的工厂代码太少,请问自己,仅仅创建内联实例会给您带来什么好处。如果收益大于增加代码的成本,那么不要担心。

我个人会避免服务地点,或者如果你真的必须使用它,我会把它隐藏在工厂后面。服务位置往往容易被滥用,并且可能导致您的容器找到它应该与之无关的代码。

为方便起见,某些容器允许您在创建组件实例时指定容器使用的工厂。在这种情况下,您的类可以直接依赖IBar,但是当您需要新实例时,您的容器会调用IBarCreator。例如,温莎城堡的API中有UseFactoryUseFactoryMethod方法。

答案 2 :(得分:2)

这完全取决于您的具体情况和您的需求 我认为最常用的方法是,如你所提到的,factory 如果你正在使用IoC框架(例如Ninject,或Sprint.Net,Castle Windsor等,请参阅here),服务定位器也是一个可行的解决方案。

答案 3 :(得分:2)

如果您遇到冗余工厂接口问题,可以在此处采用两种方法。

使用泛型使其可重复使用:

interface IFactory<T>
{
 T Create();
}

class DefaultConstructorFactory<T> : IFactory<T>, where T: new()
{
 public T Create() { return new T();}
}

或者使用匿名函数作为工厂:

public void DoSomething(Func<IBar> barCreator)
{
 var newBar = barCreator();
 //...
}

答案 4 :(得分:0)

当你说“我希望我的Foo课程能够创建Bar”时,我觉得还有一个更重要的规则是“分离关注”。所以你应该把类创建的任务委托给别的东西,Foo不应该担心那个任务。