根据SOLID原则,类不能依赖于其他类,必须注入依赖项。这很简单:
class Foo
{
public Foo(IBar bar)
{
this.bar = bar;
}
private IBar bar;
}
interface IBar
{
}
class Bar: IBar
{
}
但是,如果我希望我的Foo类能够创建Bar,而不知道IBar背后的确切实现呢? 我可以在这里考虑4个解决方案,但所有这些解决方案似乎都有缺点:
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类的代码太少了。 那么你怎么看,哪个最好?
答案 0 :(得分:3)
在这种情况下,我喜欢“注入”Func<IBar>
。像这样:
class Foo
{
public Foo(Func<IBar> barCreator)
{
this.bar = barCreator();
}
private IBar bar;
}
interface IBar
{
}
答案 1 :(得分:3)
这就是制造工厂的原因。
如果您觉得您的工厂代码太少,请问自己,仅仅创建内联实例会给您带来什么好处。如果收益大于增加代码的成本,那么不要担心。
我个人会避免服务地点,或者如果你真的必须使用它,我会把它隐藏在工厂后面。服务位置往往容易被滥用,并且可能导致您的容器找到它应该与之无关的代码。
为方便起见,某些容器允许您在创建组件实例时指定容器使用的工厂。在这种情况下,您的类可以直接依赖IBar
,但是当您需要新实例时,您的容器会调用IBarCreator
。例如,温莎城堡的API中有UseFactory
和UseFactoryMethod
方法。
答案 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不应该担心那个任务。