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

时间:2011-03-26 06:46:17

标签: dependency-injection

我一直在阅读很多关于依赖注入的想法,认为它可能是一些非常先进的编程方式,但是我看不出只是避免全局状态之间的区别,因为当没有全局状态然后你被迫通过在对象的所有依赖中。

有人可以向我解释,因为我认为我可能会忽略关于依赖注入的问题吗?

6 个答案:

答案 0 :(得分:21)

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

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

你可以将这种解耦概括为不仅仅是避免使用全局变量。请使用以下代码:

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。您解密的代码越多,您需要进行的代码更改就越少。

答案 1 :(得分:5)

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

因此,这里的价值是关注点的分离。这对于测试何时注入真实对象的模型非常有用。

答案 2 :(得分:2)

依赖注入是一种实现控制模式反转的方法,用于避免依赖解析的全局状态。您可以使用控制模式的反转,有或没有依赖注入。是的,不使用全局变量是您使用依赖注入的等式天气的重要部分。

依赖注入实际上只不过是你以自底向上的方式编写程序,其中应用程序的最顶层部分解决了所有子系统的依赖关系。假设我有一个带有依赖注入配置的程序,如:

 <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/>

这与手工创建对象没有什么不同:

 public static void main() {
    World world = new World();
    world.player1 = new LocalPlayer();
    world.player2 = new NetworkPlayer();

    world.play();
 }

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

依赖注入框架将您的程序从命令式样式代码更改为依赖性的声明式样式语言。因此,您正在通过这种声明性语言编写程序,并且可以使用许多其他功能来增强该程序。

让框架解决构造顺序和循环依赖关系等功能。声明外部配置并将值注入声明的对象(即属性文件,XML配置等),这非常好。所有这些共同使得依赖注入框架非常引人注目,可以自己完成所有这些。

答案 3 :(得分:1)

另一件事是依赖注入通常会创建单个对象。在服务和DAO等情况下,您永远不会想要多个对象。它也很好(例如通常在应用程序启动时,在春季)实例化,因此您可以在需要时使用它。

答案 4 :(得分:1)

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

通过使用依赖注入,您可以提供这些较小的组件,测试它们并以标准方式将它们链接在一起。这种结果导致了更加光滑,分离的设计。反过来,您的项目开始变慢,因为您从未使用过高复杂度的代码(至少在理论上),这种生产率仍然很高。

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

答案 5 :(得分:0)

使用依赖注入看起来就像使用Windows注册表一样。您使用所需的东西加载注册表,然后将它们拉出来并在某个模块中使用它们。

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

假设您的Dependency注册表中有20个项目。数据库,记录器,异常处理程序等。

现在在给定的模块中,您没有IDEA您的模块使用哪些依赖服务。您的上下文进一步丢失,因为您在运行代码时不知道依赖注册表中的内容!

我在这里看不到任何好处。它只是使调试变得不可能。