Avdi Grimm 在他的书Object On Rails中有一些很好的例子。
你会在这里和这里找到为什么他不选择回调选项,以及如何通过覆盖相应的 ActiveRecord 方法来摆脱它。
在你的情况下,你最终会得到类似的东西:
class Order < ActiveRecord::Base
def save(*)
normalize_card_number if paid_with_card?
super
end
private
def normalize_card_number
#do something and assign self.card_number = "XXX"
end
end
[在您评论“这仍然是回调”之后更新]
当我们谈到域逻辑的回调时,我理解ActiveRecord
回调,如果您认为 Mongoid 引用者的引用指向其他内容,请纠正我,如果在某处我没有找到“回调设计”。
我认为ActiveRecord
回调在大多数(整个?)部分只不过是我之前的示例可以摆脱的语法糖。
首先,我同意这个回调方法隐藏了它们背后的逻辑:对于不熟悉的人来说ActiveRecord
,他必须学习它才能理解代码,使用上面的版本,它很容易理解和测试。
ActiveRecord
对于回调他的“常见用法”或它们可以产生的“脱钩感觉”,这可能是最糟糕的。回调版本一开始可能看起来不错,但随着您将添加更多回调,将更难以理解您的代码(它们以什么顺序加载,哪个可能会停止执行流程等)并对其进行测试(您的域逻辑与ActiveRecord
持久性逻辑相结合)。
当我阅读下面的示例时,我对这段代码感到难过,它很臭。我相信如果你在做 TDD/BDD,你可能不会得到这个代码,如果你忘记了ActiveRecord
,我想你会简单地编写card_number=
方法。我希望这个例子足够好,不要直接选择回调选项,先考虑设计。
关于 MongoId 的引用,我想知道为什么他们建议不要将回调用于域逻辑,而是将其用于排队后台作业。我认为排队后台作业可能是域逻辑的一部分,并且有时可能会比回调更好地设计(比方说观察者)。
最后,从面向对象编程设计的角度来看,关于如何使用 Rail 使用 / 实现 ActiveRecord 存在一些批评,这个答案包含关于它的很好的信息,你会更容易找到。您可能还想检查 datamapper设计模式/ ruby 实现项目,它可以替代 ActiveRecord(但要好多少)并且没有他的弱点。