【发布时间】:2017-03-10 07:41:18
【问题描述】:
我知道你应该依赖抽象而不是具体的实现,但我也知道YAGNI 原则。我有时会发现自己很难协调这两者。
考虑以下类;
public class Foo
{
public void DoFoo()
{
}
//private foo stuff
}
public class Bar
{
private readonly Foo _foo;
public Bar()
{
_foo = new Foo();
}
}
“Bar”是我感兴趣的类;显然有问题,Bar 正在实例化 Foo 的一个实例,所以让我重构一下;
public class Bar
{
private readonly Foo _foo;
public Bar(Foo foo)
{
_foo = foo;
}
}
很好,但是 Bar 的构造函数仍然依赖于 Foo,一个具体的实现。我什么都没得到(是吗?)。为了解决这个问题,我需要使 foo 成为一个抽象,这就是我的问题开始的地方。
我找到的每个示例总是(可以理解)演示使用抽象的构造函数注入。我完全支持防御性编程,但假设我不需要除 Foo 之外的任何其他实现(测试替身不算数)。创建一个“IFoo”接口或者一个“FooBase”抽象类肯定违反了YAGNI原则?我会为一个可能的未来场景做点什么,我以后总是可以这样做的,例如
public abstract class Foo
{
public abstract void DoFoo();
//private foo stuff
}
public class Foo1:Foo
{
public override void DoFoo()
{
}
}
这不会破坏 Bar,我什至可以为界面执行此操作,前提是我放弃了“I”约定(我对此越来越持怀疑态度),例如
public interface Foo
{
void DoFoo();
}
public abstract class FooBase:Foo
{
public abstract void DoFoo();
//private foo stuff
}
public class Foo1:FooBase
{
public override void DoFoo()
{
}
}
注入具体实现有什么问题,因为我可以在稍后阶段将其重构为抽象(假设我给抽象赋予与具体实现相同的名称)?
注意:我知道“I”接口命名约定的参数,这不是我问题的重点。我也知道,让 Foo 成为一个抽象类会在我之前实例化它的任何地方破坏代码,但假设我广泛使用 DI,所以我只需要更改 DI 容器注册,如果我可能不得不做一些事情我将介绍 Foo 的新实现。
【问题讨论】:
-
您可以在 DI 容器中将一个类绑定到自身,而无需创建相应的接口(在 Ninject 中就是这样做的)。在我的脑海里并没有错。视项目规模而定!
-
#adaam - 感谢您的回复,为什么规模可能是一个因素?您是否认为较大的项目更有可能引入新的要求,从而导致新的实施?
-
为了建立@adaam 提出的观点,您希望由 DI 容器提供具体类型的另一个原因是管理它们的生命周期。如果您使用的对象不包含内部状态,那么将它们作为单例传递并没有明显的错误,尤其是当它们具有昂贵的实例化成本时。
-
@mark_h 是的,抱歉措辞不正确!我指的是项目中代码模块的应用程序数量。如果您正在为单次使用的应用程序设计一个类,并且您知道您不太可能再次使用它,那么连接它就没有什么意义了。我了解到,软件开发既要务实,又要按合同进行设计,这延伸到 DI
-
@mark_h - 是的,这行得通。无论您使用接口还是使用虚拟方法的非密封类似乎都是一个完全不同的讨论。我总是选择接口(即使用接口)并在接口后面的类型上使用继承。这样,消费者仍然只知道合同而不是类型(这可能更难以替换,也可能更难以模拟)。
标签: c# oop dependency-injection