8

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

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

    private IBar bar;
}

interface IBar 
{
}

class Bar: IBar 
{
}

但是,如果我希望我的 Foo 类能够创建 Bar,而不知道 IBar 背后的确切实现,该怎么办?我可以在这里想到 4 种解决方案,但它们似乎都有缺点:

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

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 类的代码太少了。那你怎么看,哪个最好?

4

5 回答 5

3

这就是制造工厂的目的。

如果你觉得你的工厂的代码太少,问问自己它给你带来了什么好处,而不是仅仅创建内联实例。如果好处超过了添加代码的成本,那么不要担心。

我个人会避开服务地点,或者如果你真的必须使用它,我无论如何都会把它藏在工厂后面。服务位置往往很容易被滥用,并可能导致您的容器进入与它无关的代码中。

为方便起见,某些容器允许您指定容器在创建组件实例时要使用的工厂。在这种情况下,您的类可以直接依赖,IBar但您的容器会IBarCreator在需要新实例时调用。例如, Castle Windsor在他们的 API 中有方法UseFactory和方法。UseFactoryMethod

于 2011-08-26T11:42:04.420 回答
3

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

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

    private IBar bar;
}

interface IBar 
{
}
于 2011-08-26T11:35:10.450 回答
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();
 //...
}
于 2011-08-26T11:37:33.393 回答
2

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

于 2011-08-26T11:33:50.077 回答
0

我觉得当你说“我希望我的 Foo 类能够创建 Bar 的”时,另一个规则就是“关注点分离”。因此,您应该将创建类的任务委托给其他事情,而 Foo 不应该担心该任务。

于 2011-08-26T11:33:42.143 回答