在Rails中使用域逻辑回调的优缺点

Dav*_* J. 20 ruby-on-rails mongoid

您认为使用域逻辑回调的优缺点是什么?(我在Rails和/或Ruby项目的上下文中讨论.)

为了开始讨论,我想在回调中Mongoid页面中提到这个引用:

使用域逻辑的回调是一种糟糕的设计实践,并且当链中的回调暂停执行时,可能导致难以调试的意外错误.我们建议只将它们用于跨领域的问题,例如排队后台工作.

我很想听听这个说法背后的争论或辩护.它是否仅适用于Mongo支持的应用程序?或者它旨在应用于数据库技术?

似乎"Ruby on Rails指南"对ActiveRecord验证和回调可能不同意,至少在涉及关系数据库时.举个例子:

class Order < ActiveRecord::Base
  before_save :normalize_card_number, :if => :paid_with_card?
end
Run Code Online (Sandbox Code Playgroud)

在我看来,这是一个实现域逻辑的简单回调的完美示例.它似乎快速有效.如果我要接受Mongoid的建议,那么这个逻辑会转向哪里?

nat*_*vda 28

我真的很喜欢为小班使用回调.我发现它使一个类非常易读,比如像

before_save :ensure_values_are_calculated_correctly
before_save :down_case_titles
before_save :update_cache
Run Code Online (Sandbox Code Playgroud)

现在可以清楚地知道发生了什么.

我甚至觉得这个可以测试; 我可以测试方法本身是否有效,我可以单独测试每个回调.

我坚信类中的回调应该用于属于该类的方面.如果你想在保存时触发事件,例如,如果对象处于特定状态时发送邮件,或者记录,我会使用观察者.这尊重单一责任原则.

回调

回调的优点:

  • 一切都在一个地方,所以这很容易
  • 非常易读的代码

回调的缺点:

  • 因为一切都是一个地方,所以很容易打破单一责任原则
  • 可以为重型课程做准备
  • 如果一个回调失败会发生什么?它仍然沿着链条?提示:确保您的回调永不失败,或以其他方式将模型的状态设置为无效.

观察员

观察者的优势

  • 非常干净的代码,你可以为同一个类创建几个观察者,每个观察者都做不同的事情
  • 观察者的执行没有耦合

观察者的缺点

  • 起初,如何触发行为可能会很奇怪(看看观察者!)

结论

简而言之:

  • 使用回调来处理与模型相关的简单内容(计算值,默认值,验证)
  • 使用观察者进行更多的交叉行为(例如发送邮件,传播国家......)

和往常一样:所有的建议必须采取一些盐.但根据我的经验,观察者的表现非常好(并且也鲜为人知).

希望这可以帮助.


Dav*_* J. 9

编辑:我已经将我的答案结合在一些人的建议上.

摘要

基于一些阅读和思考,我已经对我所相信的一些(暂定的)陈述:

  1. 语句"使用域逻辑的回调是一种糟糕的设计实践"是错误的,如所写的那样.它夸大了这一点.回调可以是适当使用的域逻辑的好地方.这个问题不应该是如果域模型的逻辑应该在回调中去,这是什么样的领域逻辑有道理进去.

  2. 声明"使用域逻辑的回调...可能导致在链暂停执行中的回调"时为难以调试的意外错误.

  3. 是的,回调可能会导致影响其他对象的连锁反应.在某种程度上,这是不可测试的,这是一个问题.

  4. 是的,您应该能够测试业务逻辑,而无需将对象保存到数据库.

  5. 如果一个对象的回调因为你的敏感性而变得过于膨胀,那么可以考虑其他设计,包括(a)观察者或(b)辅助类.这些可以干净地处理多个对象操作.

  6. "只使用[回调]来解决交叉问题,例如排队后台工作"的建议很有趣,但却被夸大了.(我回顾了跨领域的问题,看看我是否可能会忽视某些事情.)

我还想分享一些我对博客文章的反应,我已经阅读了关于这个问题的讨论:

对"ActiveRecord的回调毁掉了我的生活"的反应

Mathias Meyer的2010年帖子,ActiveRecord的Callbacks毁了我的生活,提供了一个视角.他写:

每当我开始在Rails应用程序中为模型添加验证和回调时[...]它只是感觉不对.感觉就像我正在添加不应该存在的代码,这会使一切变得复杂得多,并且变成隐式代码.

我发现最后一个声明"明确变成隐含代码"是一种不公平的期望.我们在这里谈论Rails,对吧?!如此大量的增值是关于Rails"神奇地"做事情,例如没有开发人员必须明确地做.享受Rails的成果并批评隐含代码似乎并不奇怪吗?

仅根据对象的持久性状态运行的代码.

我同意这听起来很令人讨厌.

难以测试的代码,因为您需要保存对象以测试业务逻辑的各个部分.

是的,这使测试变得缓慢而困难.

因此,总而言之,我认为Mathias在火灾中添加了一些有趣的燃料,尽管我并未发现所有这些都令人信服.

对"疯狂,异端和令人敬畏:我写Rails应用程序的方式"的反应

在James Golick的2010年帖子中,Crazy, Heretical 和Awesome:The Way I Write Rails Apps,他写道:

此外,将所有业务逻辑耦合到持久性对象可能会产生奇怪的副作用.在我们的应用程序中,当创建某些内容时,after_create回调会在日志中生成一个条目,用于生成活动源.如果我想创建一个没有记录的对象,比如在控制台中,该怎么办?我不能.拯救和伐木永远结婚,永恒.

后来,他找到了它的根源:

解决方案实际上非常简单.对问题的简化解释是我们违反了单一责任原则.因此,我们将使用标准的面向对象技术来分离模型逻辑的关注点.

我真的很感激他通过告诉你何时适用以及什么时候没有:

事实是,在一个简单的应用程序中,肥胖的持久性对象可能永远不会受到伤害.事情比CRUD操作复杂得多,这些事情开始堆积起来并成为痛点.