Dependency injection 依赖注入与不使用全局变量有何不同?

Dependency injection 依赖注入与不使用全局变量有何不同?,dependency-injection,Dependency Injection,我读了很多关于依赖注入的书,认为这可能是一种非常高级的编程方式,但我看不到仅仅避免全局状态之间的区别,因为当没有全局状态时,您必须将所有依赖项传递给对象 有人能给我解释一下吗,因为我认为我可能忽略了依赖注入是什么?我对依赖注入的理解是,您忽略了创建对象的细节,只声明需要这样一个对象。例如,一个框架将在需要之前设置这个对象 所以这里的值是关注点的分离。当您将注入真实对象的模型时,这对于测试非常有用。另一件事是依赖项注入通常会创建单例对象。在服务和DAO这样的情况下,您永远不希望有多个对象。很高兴它

我读了很多关于依赖注入的书,认为这可能是一种非常高级的编程方式,但我看不到仅仅避免全局状态之间的区别,因为当没有全局状态时,您必须将所有依赖项传递给对象


有人能给我解释一下吗,因为我认为我可能忽略了依赖注入是什么?

我对依赖注入的理解是,您忽略了创建对象的细节,只声明需要这样一个对象。例如,一个框架将在需要之前设置这个对象


所以这里的值是关注点的分离。当您将注入真实对象的模型时,这对于测试非常有用。

另一件事是依赖项注入通常会创建单例对象。在服务和DAO这样的情况下,您永远不希望有多个对象。很高兴它已经被实例化了(通常是在应用程序启动时,在春天),所以你可以在需要的时候使用它

依赖注入是关于解耦代码的

当您通过传递参数来避免使用全局变量时,您就是在解耦代码。您正在删除代码对全局变量的依赖关系

您可以将这种解耦推广到不只是避免全局变量。以下面的代码为例:

def foo(arg):
   return ClassBar(arg).attr

foo(1)
函数
foo
依赖于或紧密耦合到
ClassBar
。这不好的原因是,在以下情况下,您将被迫更新
foo

  • 构造
    ClassBar
    的参数发生变化
  • 您想将
    ClassBar
    更改为其他内容
  • 另一段代码希望从其他对象访问
    attr
如果代码被重写:

def foo(instanceBar):
   return instanceBar.attr

foo(ClassBar(1))

您已将联轴器推到来电者面前。这从
foo
的定义中删除了依赖项。在上述情况下,您无需更新
foo
。解耦的代码越多,所需的代码更改就越少。

依赖项注入是实现控制反转模式的一种方法,可以避免依赖项解析的全局状态。您可以使用带或不带依赖项注入的反转控制模式。是的,无论是否使用依赖注入,不使用全局变量都是等式的一个重要部分

依赖项注入实际上就是以自下而上的方式编写一个程序,其中应用程序的最顶层部分解析所有子系统的依赖项。假设我有一个具有依赖注入配置的程序,如:

 <bean id="world" class="com.game.World" start-method="play">
    <property name="player1" ref="player1"/>
    <property name="player2" ref="player2"/>
 </bean>

 <bean id="player1" class="com.game.LocalPlayer"/>
 <bean id="player2" class="com.game.NetworkPlayer/>

使用依赖项注入仅仅意味着编写上述代码是为您处理的。在这个简单的例子中,你不可能只使用代码而不使用它,但是在更大的程序中,它确实为你节省了很多时间。它还防止您或团队成员使用快捷方式,因为它不像您编写代码时那样开放

依赖项注入框架将程序从命令式代码更改为依赖项的声明式语言。因此,您正在通过这种声明性语言编写一个程序,您可以用许多其他特性来扩展它


让框架为您解析构造顺序和循环依赖关系等功能。声明外部配置并将值注入声明的对象(即属性文件、XML配置等),这非常好。所有这些加在一起,使得依赖注入框架比单独完成所有这些工作更具吸引力。

假设您是一名优秀的程序员,您希望能够轻松地测试和更换您的部件系统。最好的方法是采用模块化设计。也就是说,你想把你的问题分解成更小的问题,你可以解决这些问题并保持无bug

通过使用依赖项注入,您可以提出这些较小的组件,测试它们,并以标准方式将它们链接在一起。这将导致更流畅、解耦的设计。反过来,您的项目不会开始变慢,因为您从未使用过高复杂度的代码(至少在理论上是这样),这样的生产率仍然很高


如果您是一名熟练的开发人员,您可以使用singleton模式(和其他模式)来获得大部分相同的好处。但是,您的整个团队需要具备相同的技能,否则您将再次获得耦合设计和低吞吐量。

使用依赖项注入看起来就像使用Windows注册表一样。你把你想要的东西加载到注册表中,然后把它们拉出来,在某个模块中使用它们

然而,它破坏了面向对象的代码

假设您的依赖项注册表中有20项。数据库、记录器、异常处理程序等

现在,在一个给定的模块中,您不知道您的模块使用了哪些依赖项服务。您的上下文将进一步丢失,因为您不知道在运行代码时依赖项注册表中将包含什么


我看不出这有什么好处。这只会使调试变得不可能。

呵呵,这是我看到的第四个DI问题。看到我贴在这里的这个大答案了吗。几个IoC/DI框架允许您选择生存期。您可以让容器始终注入相同的实例(Singleton),在每个请求上创建一个新实例,在每个线程上创建一个实例,或者(至少在Unity中)注入一个自动工厂委托。-1用于不仅使用传统上不好的示例foo和bar,而且区分大小写使用它们!:$@Tchalvak:惯用Python使用大写的单词作为类名,小写的单词作为实例和值(除非您谈论全局)。在所有类似的情况下,在处理类和实例时都会使用不同的大小写。@Tchalvak:Why
foo
bar
 public static void main() {
    World world = new World();
    world.player1 = new LocalPlayer();
    world.player2 = new NetworkPlayer();

    world.play();
 }