在Ruby中依赖注入的一个好习惯是什么?

时间:2015-10-25 04:24:57

标签: ruby oop dependency-injection

我一直在阅读Sandi Metz的 Ruby实用面向对象设计以及许多在线讨论Ruby设计的网站。我很难完全理解的是实现依赖注入的正确方法。

互联网充斥着博客文章,解释了依赖注入如何在我认为非常局部的方式中起作用。

我知道这应该是坏事:

class ThisClass
  def initialize
    @another_class = AnotherClass.new
  end
end

虽然这是一个解决方案:

class ThisClass
  def initialize(another_class)
    @another_class = another_class
  end
end

我可以像这样发送AnotherClass.new:

this_class = ThisClass.new(AnotherClass.new)

这是Sandi Metz至少推荐的方法。我不明白的是那条线应该去哪里?它必须走到某个地方,通常在这个示例中,显示的是一条完全放在任何类,方法或模块之外的行,就好像我只是在IRB中手动输入它以进行测试。

This post(以及其他人)提出了这种不同的方法:

class ThisClass
  def another_class
    @another_class ||= AnotherClass.new
  end
end

Jamis Buck会采用类似的方法:

class AnotherClass
end

class ThisClass
  def another_class_factory(class_name = AnotherClass)
    class_name.new
  end
end

然而,这两个例子都保留了ThisClass中的AnotherClass名称,Sandi Metz说这是我们试图避免的主要内容之一。

那么这样做的最佳做法是什么?我应该在我的应用程序中创建一个“依赖”模块,其中填充的方法是每个类的对象的工厂吗?

1 个答案:

答案 0 :(得分:5)

  

我很难完全理解的是实现依赖注入的正确方法。

我认为"最合适的"实现是遵循面向对象设计的SOLID principles的实现。在这种情况下,主要是依赖性倒置原则

在这方面,这是违反DIP(1)的唯一解决方案:

class ThisClass
  def initialize(another_class)
    @another_class = another_class
  end
end

在所有其他情况下,ThisClassAnotherClass具有硬依赖性,没有它就无法运行。此外,如果我们希望将AnotherClass替换为第三个,我们需要修改ThisClass,这违反了开放封闭原则

当然,在上面的示例中,命名参数和实例变量another_class并不理想,因为我们现在(并且不需要知道)传递给我们的对象是什么,只要它响应预期的界面。这就是多态性之美。

考虑以下示例,取自此ThoughtBot video on DIP

class Copier
  def initialize(reader, writer)
    @reader = reader
    @writer = writer
  end

  def copy
    @writer.write(@reader.read_until_eof)
  end
end

您可以在此处传递分别回复{​​{1}}和reader的所有writerread_until_eof个对象。这使您可以完全自由地使用不同的读写实现组合构建业务逻辑,即使在运行时也是如此:

write

这将我们带到您的下一个问题。

  

它必须去某个地方,通常在这个例子中,显示的是一条完全不在任何类,方法或模块之外的线[...]

你是对的。虽然对象思维涉及使用隔离良好,解耦和可组合的对象对域进行建模,但您仍需要定义这些对象如何交互,以实现任何业务逻辑。毕竟,除非我们编写它们,否则拥有可组合对象是没有用的。

此处经常进行的类比是将您的对象视为 actors 。你是导演,你仍然需要为演员创建一个脚本(2),让他们知道如何互相交流。

也就是说,您的应用程序需要入口点脚本启动的位置。这可能本身就是一个对象 - 通常是一个抽象的对象。在命令行应用程序中,它可以是您的经典Copier.new(KeyboardReader.new, Printer.new) Copier.new(KeyboardReader.new, NetworkPrinter.new) 类,在Rails应用程序中,它可以是您的控制器。

这一开始可能看起来很奇怪,因为对象思维的重点是建模具体的域对象,关于这个主题的大量所有着作都致力于这项工作,但只记得 actor-script < / em>比喻,你将会在路上。

我强烈建议你拿起书Object Thinking。它很好地解释了面向对象设计背后的思维模式,没有它,了解特定于语言的实现细节变得相当无用。

(1):值得注意的是,有些支持者认为将另一个类的实例存储在实例变量和反模式中,但在Ruby中,这是相当惯用的。

(2):我不确定这是否是编程中 script 一词的起源,但也许某些历史学家可以对此有所了解。