2011-02-01 43 views
10

在我目前工作的应用程序中,我看到很多观察者。这确实给我造成了很多麻烦,同时我改变了代码,添加了新的功能,因为这些观察者导致了大量的副作用。Rails观察者 - 什么时候以及何时不在Rails中使用观察者

我想知道一些场合,要求观察者以及那些有经验的人或经验丰富的人,当他们被诱惑陷入观察者陷阱时。

您的宝贵经验,战争故事和想法是需求。请大声喊出来!

+0

对于那些针对观察者的人,请考虑使用[使用案例](http://webuild.envato.com/blog/a-case-for-use-cases/),这是一系列(业务逻辑)步骤执行。推出自己的用例并不困难,但也有一些体面的宝石,例如[solid_use_case](https://github.com/mindeavor/solid_use_case)或[use_case](https://github.com/cjohansen/use_case) – Dennis 2016-04-03 22:07:28

回答

28

我觉得观察者得到一个坏的说唱很大程度上是因为人们将它们与ActiveRecord生命周期回调混为一谈是同样的事情。我同意很多关于生命周期回调的流行观点,这些观点很容易被误用,让自己陷入混乱,但我个人非常喜欢观察员将模型类别保留下来,这不是模型的核心责任。这里有一个提示:Rails的观察者部分受面向方面编程的启发 - 他们是关于交叉问题的。如果您将业务逻辑放在与观察模型紧密耦合的观察者中,那么您做错了IMO。

它们非常适合保持杂波出模型类,如缓存过期(清洁工),各种种类的通知,活动流的更新,拉开了后台作业跟踪定制分析事件,保暖缓存等。

我强烈地不同意BlueFish关于观察者很难适当地进行单元测试。这正是将它们与生命周期回调区分开来的最大的一点:你可以孤立地测试观察者,这样做可以阻止你陷入BlueFish所指的许多状态和秩序沉重的设计陷阱中(我认为这更常见生命周期回调的真实情况)。

这里是我的药方:

  1. 禁止在您的测试套件中的所有观察员默认。他们不应该让你的模型测试复杂化,因为无论如何他们应该有不同的担忧。你不需要单独测试观察者实际上触发的事情,因为ActiveRecord的测试套件会这样做,并且你的集成测试将覆盖它。如果你的真的是认为有一个很好的理由让你的单元测试的一小部分观察者,但它可能是一个滥用或设计问题的指标。
  2. 仅对您的集成测试启用观察员。整合测试当然应该是全面的,你应该像在其他方面一样验证观察者的行为。
  3. 单元测试你的观察者类别在隔离(直接调用after_create等方法)。如果观察者不是其观察模型的商业逻辑的一部分,那么它可能不会对模型实例的状态细节有太多依赖性,也不需要太多的测试设置。如果您有信心确信您的集成测试涵盖了您最关心的内容,那么您经常可以在这里模拟合作伙伴。

这是一个使用的RSpec的应用我的标准样板spec/support/observers.rb

RSpec.configure do |config| 
    # Assure we're testing models in isolation from Observer behavior. Enable 
    # them explicitly in a block if you need to integrate against an Observer -- 
    # see the documentation for {ActiveModel::ObserverArray}. 
    config.before do 
    ActiveRecord::Base.observers.disable :all 
    end 

    # Integration tests are full-stack, lack of isolation is by design. 
    config.before(type: :feature) do 
    ActiveRecord::Base.observers.enable :all 
    end 
end 

而且here is a real-world example,我希望说明了使用观测,并且无痛苦测试它一个很好的案例。

6

恕我直言 - 观察员吸

我会去通过一些原因,我认为他们这样做。请注意,这通常适用于before_x或after_x方法的使用 - 这是更一般Observer的零碎示例。

使得很难正确写单元测试

通常,当写单元测试,如果正在测试一个特定的功能块。然而,为了测试观察者,你需要'触发'事件以便测试它,有时这是非常不方便的。

E.g.如果您将观察者连接到before_save,然后触发代码,则需要保存模型。考虑到你可能正在测试业务逻辑而不是持久性,这使得测试变得棘手。如果你嘲笑保存,你的触发器可能无法工作。如果你让它保存,那么你的测试很慢。

要求国家

一个事实,即观察者往往难以测试继,观察员往往还需要大量的状态。原因是因为观察者的逻辑试图区分各种“商业事件”,唯一的方法就是查看对象的状态。这需要在测试中进行很多设置,因此使测试变得艰难,乏味和有问题。

意想不到的后果

毫无疑问,你刚刚经历了,因为你可以连接多观察,你不知道有什么可以触发各种行为。这会导致意想不到的后果,您只能通过集成/系统测试(慢速反馈)才能获得结果。追踪你的观察者也不是很有趣。

订购假设的问题

没有保证时,观看者可踢。你只能保证,这将拉开序幕。如果您将隐式订单作为业务规则的一部分,则观察者是错误的。

通过挂钩观测信息低劣的设计

添加的东西会导致不良的设计。它往往会引导您将所有内容都保存起来,删除,创建事件 - 虽然方便,但也很难理解。例如。保存用户可能意味着您正在更新用户的详细信息,或者这可能意味着您要为其添加新的帐户名称。了解你可以专门做什么对象是你拥有方法和有意义的基于动作的名字的原因之一。如果所有事情都是观察者,那么这会丢失,所有事情现在都对事件做出反应,并且在观察逻辑中,您倾向于区分属于哪个业务事件的事件。

有些地方的观察员很好,但通常是个例外。要明确地完成什么可以做得更好,而不是通过回调隐式地编码逻辑。

5

我部分同意BlueFish,因为观察者可能会引入不必要的复杂性,但是观察者对于从对象中分离问题很有用。

例如,在支付AR模型中,人们可能希望在创建后交付收据。使用常规的AR after_create回调,如果deliver_receipt方法失败,付款不会写入数据库 - 哎呀!然而在观察员中,付款仍然会得到保存。 有人可能会认为失败应该通过救援来解决,但我仍然认为它不属于那里;它属于观察者。

+0

有趣的POV。不过,作为after_create的一部分,将收据生成方式推迟为delayed_job并不会更好。这样,避免了观察者,代码也可以维护。 – karthiks 2011-11-18 04:30:52

+0

将其添加为延迟作业当然是一种选择,但发送电子邮件并不是一项非常昂贵的操作,因此IMO不会增加这种复杂性。 – Zubin 2011-11-19 05:59:33