我和我的一位同事就依赖注射问题进行了激烈辩论,并意识到我并不完全了解有关该主题的所有事实。
所以,拿这个代码(只是你知道,我们正在使用Castle Windsor)
IPlayerService service = Container.Resolve<IPlayerService>();
上面的代码显然是使用IoC的DI的一个例子。
但是,请参阅下面的代码(更新:假设我通过构造函数传递所有外部依赖项):
var playerClient = new PlayerClient();
var playerSkinClient = new PlayerSkinClient();
IPlayerService service = new PlayerService(playerClient, playerSkinClient);
我的论点是上面的代码是DI模式的一个例子,DI可以在没有IoC的情况下存在。
现在,我的同事并没有完全不同意我的观点,但他说上面的代码不是涉及DI的任何模式的例子。
那么,DI可以作为一种模式而不需要任何额外的框架吗?
如果是,上面的代码是一个例子吗?
最后,定义了DI模式(如果存在),没有容器的概念。
我今晚稍后会更全面地回答和评论,但感谢大家到目前为止经过深思熟虑的答案和评论!
答案 0 :(得分:15)
根据Martin Fowler所说,Dependency Injection是抽象概念的具体实现的另一个名称,称为控制反转(full article)。
从根本上说,所有DI意味着:一个类依赖于正常工作的对象被初始化并从外部传递到它,而不是类本身负责获取/初始化这些对象。如何实现这一点的具体细节超出了模式的范围。
IoC是同样的事情,但正如Martin Fowler所说,“控制反转”是描述正在发生的事情的一种非常迟钝的方式。
我个人认为“依赖注入”并不是更好。我会将其描述为“正确使用构造函数”。
非IoC /非DI的示例:
class Foo
{
void DoSomething()
{
DbConnection myConnection = new DbConnection(ConfigurationSettings...);
myConnection.ExecuteCommand();
}
}
使用IoC / DI也是如此:
class Foo
{
private DbConnection myConnection;
public Foo(DbConnection conn)
{
myConnection = conn;
}
void DoSomething()
{
myConnection.ExecuteCommand();
}
}
我恭敬地不同意那些认为它不是真正的IoC / DI的人,除非你有一个明确的binder /汇编程序/什么让你将注入的类型与具体的实现连接起来,因为接收依赖的类没有'知道区别。是否在外部类或外部配置文件中安排依赖项是一个实现细节。
答案 1 :(得分:4)
那么,DI可以仅用作一种模式而不需要任何额外的框架吗?
是的,绝对。
如果是,上面的代码是否是一个例子?
是的,绝对。
最后,定义DI模式(如果存在),没有容器的概念。
对象被赋予它所依赖的一切;它的依赖性被注入其中。
答案 2 :(得分:3)
在Java世界中,依赖注入通常涉及框架,容器等。但是没有必要使用所有这些机器。
依赖注入的本质是一个类,它可以被赋予它所依赖的对象,而不是在实现中对它们进行硬编码。如果你可以从类外部“注入”那些“依赖关系”,那么你就有了依赖注入。框架可能是一种很好的方法,但这不是必需的。
在Python世界中,没有依赖注入框架的文化,因此很多程序员都想知道所有的大惊小怪。对于他们来说,DI“只是”能够将对象或类传递给构造函数。
回答您的具体问题:
是的,DI可以在没有框架的情况下完成。
是的,上面的代码就是一个例子:PlayerService不知道PlayerClient或PlayerSkinClient来自何处。他们已被注入课堂。请注意,其他人已经回答了这个“否”。
见上文。
答案 3 :(得分:1)
是的,虽然与PicoContainer或其他非常小的容器相比,所有这些容器都经过测试和深思熟虑,但你最终会实现同样的功能。结束。 最好的例子是Ayende的"Building an IoC container in 15 lines"。
修改我的回答,我将改为是。我认为这里的主要认知不一致是由于开发人员混淆了许多/大多数DI / IoC框架提供了构建不像new
的对象的简洁方法这一事实,这就是他们坚持IoC思想的原因。
如前所述,Martin Fowler's是规范的定义,在其他地方也有很好的涵盖。
答案 4 :(得分:1)
有趣的是,第一个例子根本不是DI,而是Service Locator anti-pattern的一个例子 - 没有注入。
您自己的示例是纯DI - 更具体地说是构造函数注入模式的实现。
DI不是一种模式,而是一系列模式和原则。当我们手动连接所有依赖项时,我们通常将其称为穷人的DI 。换句话说,DI容器是严格可选的,但强烈建议:)
另见this description of what a DI Container brings to the table。
答案 5 :(得分:0)
答案 6 :(得分:0)
这真的不是DI的一个例子。
原因是您正在安排依赖项(在您的案例中为PlayerClient
和PlayerSkinClient
),而不是为您安排依赖项(在构造具体的{{1}时由容器传入})。这是“控制反转” - 你有一个被调用的构造函数,而不是调用它。这是使用DI模式的主要原因,并且拥有一个容器,用于对请求它们的对象进行范围,解析和放置,这是执行此操作的具体方法。
当您让容器管理此依赖项的传递时,您不需要编写和维护将依赖项传递给具体IPlayerService
的代码。这允许您进行IPlayerService
和PlayerClient
的其他实现(例如,当您想要模拟它们来构建测试时)而不必更新初始化PlayerSkinClient
的代码。这就是DI模式有用的原因。
答案 7 :(得分:0)
以下是其他一些文章,可能会按照“应该先阅读”的顺序带来一些关于这个主题的其他重要细节:
阅读完这些内容之后,我发现自己对这个主题有了更好的理解。