gpt4 book ai didi

ruby - Ruby 中依赖注入(inject)的最佳实践是什么?

转载 作者:数据小太阳 更新时间:2023-10-29 06:59:20 28 4
gpt4 key购买 nike

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

互联网上充斥着解释依赖注入(inject)如何以我认为非常片面的方式工作的博客文章。

我知道这应该是不好的:

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 说这是我们试图避免的主要事情之一。

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

最佳答案

Something I've had a hard time fully understanding is the proper way to implement dependency injection.

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

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

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

在所有其他情况下,ThisClassAnotherClass 具有硬依赖性,没有它就无法运行。此外,如果我们想用第三个替换 AnotherClass,我们需要修改 ThisClass,这违反了Open Closed Principle

当然,在上面的例子中,将参数和实例变量命名为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

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

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

这让我们想到您的下一个问题。


It has to go somewhere and generally in examples of this what's shown is a line like that being placed totally outside of any class, method, or module [...]

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

这里经常做的类比是将您的对象视为 Actor 。您是导演,您仍然需要创建一个脚本(2) 让 Actor 们知道如何相互交流。

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

乍一看这似乎很奇怪,因为对象思维的重点是建模具体的领域对象,并且关于该主题的大量著作都致力于此,但请记住 actor-script 隐喻,你会在路上。


我强烈建议你拿起这本书Object Thinking .它很好地解释了面向对象设计背后的思维方式,如果不了解特定于语言的实现细节就会变得毫无意义。


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

(2):我不确定这是否是一般编程中脚本 一词的起源,但也许一些历史学家可以对此有所启发。

关于ruby - Ruby 中依赖注入(inject)的最佳实践是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33326166/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com