Rails Observers - когда и когда не следует использовать наблюдателей в Rails - PullRequest
10 голосов
/ 01 февраля 2011

В приложении, в котором я сейчас работаю, я вижу много наблюдателей.Это действительно создает много проблем для меня, пока я делаю изменения в коде, добавляю новые функциональные возможности, так как эти наблюдатели вызывают массу побочных эффектов.

Я хотел бы знать случаи, когда требуется Observer и те люди, которые имеютопыт, эмпирический или личный, когда возникает искушение попасть в ловушку наблюдателя.

Ваш ценный опыт, военные истории и мысли востребованы.Пожалуйста, кричите!

Ответы [ 3 ]

29 голосов
/ 20 февраля 2013

Я чувствую, что наблюдатели получают плохой рэп в основном потому, что люди считают их обратными вызовами жизненного цикла ActiveRecord как одно и то же.Я согласен с мнением многих о том, что обратные вызовы жизненного цикла легко использовать неправильно, что приводит к путанице, но я лично большой поклонник наблюдателей за то, что они не допускают классы моделей, которые не являются основной обязанностью конкретной модели.Вот подсказка: наблюдатели Rails были частично вдохновлены аспектно-ориентированным программированием - они о сквозных проблемах.Если вы помещаете бизнес-логику в наблюдателей, которые тесно связаны с наблюдаемыми ими моделями, вы делаете это неправильно IMO.

Они идеально подходят для предотвращения беспорядка в классах моделей, таких как истечение срока действия кэша.(подметальные машины), уведомления различного рода, обновления потоков активности, запуск фоновых заданий для отслеживания пользовательских событий аналитики, горячие кэши и т. д.

Я категорически не согласен с BlueFish по поводу того, что наблюдателям сложно правильно выполнить модульный тест.Это как раз самая важная особенность, которая отличает их от обратных вызовов жизненного цикла: вы можете тестировать наблюдателей изолированно, и это препятствует тому, чтобы вы попали во многие ловушки, связанные с состоянием и порядком заказов, на которые ссылается BlueFish (что, опять же, я думаю, чащеtrue для обратных вызовов жизненного цикла).

Вот мой рецепт:

  1. По умолчанию отключить всех наблюдателей в вашем наборе тестов .Они не должны усложнять ваши модельные тесты, потому что у них все равно должны быть отдельные проблемы.Вам не нужно проводить модульное тестирование, которое фактически запускают наблюдатели, потому что это делает набор тестов ActiveRecord, и ваши интеграционные тесты будут охватывать его.Используйте блочную форму ActiveRecord::Base.observers.enable, если вы действительно считаете, что есть веская причина включить наблюдателя для небольшого фрагмента ваших юнит-тестов, но это, вероятно, показатель неправильного использования или проблемы проектирования.
  2. Включить наблюдателей только для ваших интеграционных тестов .Конечно, интеграционные тесты должны быть полными стеками, и вы должны проверять поведение наблюдателя в них, как и все остальное.
  3. Модульное тестирование ваших классов-наблюдателей изолированно (вызывайте методы, подобные after_createнапрямую).Если наблюдатель не является частью бизнес-логики наблюдаемой модели, он, вероятно, не будет сильно зависеть от деталей состояния экземпляра модели и не должен требовать больших настроек теста.Здесь вы можете часто издеваться над соавторами, если уверены, что ваши интеграционные тесты охватывают то, что вас больше всего интересует.

Вот мой стандартный шаблон spec/support/observers.rb для приложений, использующих RSpec:

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

И вот реальный пример , который, я надеюсь, иллюстрирует хороший пример использования наблюдателя и его тестирования безболезненно.

7 голосов
/ 16 сентября 2011

ИМХО - Наблюдатели сосут

Я рассмотрю несколько причин, по которым, я думаю, они это делают. Напомним, что в целом это относится и к использованию методов before_x или after_x, которые являются более частичными примерами общего Observer.

затрудняет написание правильных модульных тестов

Обычно, когда вы пишете модульные тесты, вы тестируете определенную часть функциональности. Однако, чтобы проверить наблюдателя, вам нужно «вызвать» событие, чтобы проверить его, а иногда это просто неудобно.

например. Если вы подключите наблюдателя к before_save, то для запуска кода вам нужно сохранить модель. Это затрудняет тестирование, учитывая, что вы можете тестировать бизнес-логику, а не постоянство. Если вы отключите сохранение, ваш триггер может не сработать. И если вы позволите сохранить, то ваши тесты будут медленными.

Требуется состояние

Исходя из того, что наблюдателей, как правило, трудно проверять, наблюдателям, как правило, также требуется много государства. Причина в том, что логика в наблюдателе пытается различить различные «деловые события», и единственный способ сделать это - посмотреть на состояние объекта. Это требует значительных настроек в ваших тестах и, следовательно, делает тестирование сложным, утомительным и проблематичным.

непреднамеренные последствия

Без сомнения, вы только что испытали это, потому что вы можете подключить несколько наблюдений, вы не представляете, что может вызвать различные виды поведения. Это приводит к непреднамеренным последствиям, которые вы можете обнаружить только при интеграции / тестировании системы (медленная обратная связь). Отслеживать ваших наблюдателей тоже не очень весело.

Проблемы с допущениями заказа

Нет гарантии того, что наблюдатель может сработать. Вам гарантировано, что он будет скинут. Если у вас есть неявный порядок как часть ваших бизнес-правил, то Наблюдатели ошибаются.

Приводит к плохому дизайну

Добавление вещей путем подключения наблюдателей приводит к плохим проектам. Это приводит к тому, что вы подключаете все, чтобы сохранять, удалять, создавать события, что, хотя и удобно, также трудно понять. Например. сохранение пользователя может означать, что вы обновляете данные пользователя, или это может означать, что вы добавляете в него новое имя учетной записи. Знание того, что вы конкретно можете сделать с объектом, является частью причины, по которой у вас есть методы и содержательные имена, основанные на действиях. Если все является наблюдателем, то это теряется, и все теперь реагирует на события, и в рамках вашей логики наблюдения вы стремитесь различить событие, принадлежащее к какому бизнес-событию.

Есть некоторые места, где наблюдатель хорош, но обычно это исключение. Гораздо лучше привить то, что можно сделать явно, чем неявно кодировать логику с помощью обратных вызовов.

5 голосов
/ 18 ноября 2011

Я частично согласен с BlueFish в том, что наблюдатели могут вносить ненужную сложность, однако наблюдатели полезны для отделения проблем от объекта.

Например, в модели Payment AR можно хотеть доставить квитанцию ​​послеСоздайте.При использовании обычного обратного вызова AR after_create, если метод delivery_receipt не удался, платеж не будет записан в базу данных - упс!Однако, по мнению наблюдателя, платеж все равно будет сохранен.Можно утверждать, что сбои должны быть обработаны спасением, но я все еще думаю, что это не относится к этому;принадлежит наблюдателю.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...