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

我一直在阅读Ruby中Sandi Metz的实用面向对象设计和许多网站在线讨论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)

这是桑迪梅斯至少推荐的方法。 我不明白的是这样的线路应该放在哪里? 它必须到某个地方,并且通常在这个例子中显示的是类似于被放置在任何类,方法或模块之外的线,就好像我只是在IRB中手动输入以用于测试目的一样。

这篇文章(除其他外)表明了这种不同的方法:

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

然而,这两个例子都在AnotherClass中保存AnotherClass的名字,Sandi Metz说这是我们试图避免的主要事情之一。

那么做这件事的最佳做法是什么? 我是否应该在我的应用程序中创建一个充满方法的“依赖项”模块,这些方法是针对每个类的对象的工厂?


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

我认为“正确”实现的最佳定义是遵循面向对象设计的SOLID原则。 在这种情况下,主要是依赖倒置原则。

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

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

在其他情况下, ThisClass有一个硬依赖AnotherClass ,没有它不能正常工作。 此外,如果我们想用AnotherClass替换AnotherClass ,我们需要修改ThisClass ,这违反了开放封闭原则。

当然,在上面的例子中,命名参数和实例变量another_class并不理想,因为我们现在不需要知道传递给我们的对象,只要它响应预期的接口即可。 这是多态的美。

考虑下面的例子,摘自DIP上的ThoughtBot视频:

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

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

在这里,您可以传递任何响应read_until_eof并分别write readerwriter对象。 这使您可以完全自由地使用不同的读写对实现来构建业务逻辑,即使在运行时也是如此:

Copier.new(KeyboardReader.new, Printer.new)
Copier.new(KeyboardReader.new, NetworkPrinter.new)

这给我们带来了你的下一个问题。


它必须去某个地方,并且通常在这个例子中显示的是类似于完全放在任何类别,方法或模块之外的线路[...]

你是对的。 尽管对象思维涉及到对具有良好隔离,分离和可组合对象的域进行建模,但仍然需要定义这些对象如何交互以实现任何业务逻辑。 毕竟,除非我们编写它们,否则组合对象并不好。

这里经常做的比喻是把你的对象看成是演员。 你是导演,你还需要为演员创建一个脚本(2),以便知道如何互相交流。

也就是说,您需要一个入口点进入您的应用程序。 脚本开始的地方。 这本身可能是一个对象 - 通常是一个抽象的对象。 在命令行应用程序中,它可以是您的经典Main类,并且在Rails应用程序中它可以是您的控制器。

起初这可能看起来很奇怪,因为对象思维的焦点是对具体领域对象进行建模,并且关于这个主题的所有着作都致力于这项工作,但只记得演员脚本隐喻,你会在你的路上。


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


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

(2):我不确定这是否是程序设计术语脚本的起源,但也许一些历史学家可以对此有所了解。

链接地址: http://www.djcxy.com/p/29925.html

上一篇: What is a good practice for dependency injection in Ruby?

下一篇: VM has multidex support, MultiDex support library is disabled