Комментарии 27
github.com/rails/rails/commit/9e4c41c903e8e58721f2c41776a8c60ddba7a0a9 — Вот этот коммит в rails 4 хоронит ActiveModel
Косяк:
class Signup include Virtus extend ActiveModel::Naming include ActiveModel::Conversion include ActiveModel::Validations
Вот кстати неплохой гем для реализации форм через модели — github.com/ClearFit/redtape
Про декоратор:
этот пример меня смущает, получается, мы должны помнить при написании контоллера, что модель должна отправлять нотификации в фейсбук и мы должны использовать декоратор, а не саму модель. По-моему, это наоборот, увеличивает сложность.
Также ожидал услышать про делегацию, на мой взгляд, самый правильный способ разбивать модель.
И в контроллере:
class CommentsController < ApplicationController def create @comment = FacebookCommentNotifier.new(Comment.new(params[:comment])) if @comment.save redirect_to blog_path, notice: "Your comment was posted." else render "new" end end end
этот пример меня смущает, получается, мы должны помнить при написании контоллера, что модель должна отправлять нотификации в фейсбук и мы должны использовать декоратор, а не саму модель. По-моему, это наоборот, увеличивает сложность.
Также ожидал услышать про делегацию, на мой взгляд, самый правильный способ разбивать модель.
По-моему довольно логичный ход. Когда я пишу код создания комментария, я знаю что мне нужно продублировать его в ФБ. В данном случае как раз Observer, использование которого многим показалось бы логичным, как раз и усложняет код. А вот декоратор как раз явно показывает намерения разработчика кода.
А если вам где-то в другом месте понадобится создать комментарий, вы уверены, что вспомните о том, что надо в фейсбук продублировать? Или у вас в одном месте будет дублироваться, а в другом — нет? Логику надо отделять от контроллеров, а тут она во всей красе. Обсервер предлагал не я, мне тоже не нравится, что он неявно обвешивает модель, но для некоторых вещей, непосредственно не связанных с функциональностью модели, наверное, имеет смысл.
Уверен. Мы же не пишем код с ИИ, чтобы он сам помнил о ТЗ. Это моя задача, как разработчика.
В данном конкретном месте, зная что необходимо дублировать в ФБ, я использую стратегию создания комментария, заодно дублирующую его в ФБ. В другом месте, если это необходимо — тоже буду. Там, где не нужно — не буду. Проблема то в чём?
В данном конкретном месте, зная что необходимо дублировать в ФБ, я использую стратегию создания комментария, заодно дублирующую его в ФБ. В другом месте, если это необходимо — тоже буду. Там, где не нужно — не буду. Проблема то в чём?
В том, что проекты не пишутся раз и навсегда, а поддерживаются и изменяются. Также есть проекты, которые просто не влезут целиком в голову и которые пишутся не один месяц. Также есть проекты, которые пишутся не в одиночку, а командой, поэтому код должен быть понятным и предсказуемым, а не «угадай, куда я это засунул».
Если это всё не про вас, вопросов нет, можете писать всё одной портянкой, зачем вам вообще контроллеры и модели, если у вас есть ТЗ и вы всё знаете?
Если это всё не про вас, вопросов нет, можете писать всё одной портянкой, зачем вам вообще контроллеры и модели, если у вас есть ТЗ и вы всё знаете?
Вы сейчас пытаетесь сказать, что когда команда работает над проектом, каждому из них знать ТЗ и архитектуру проекта — не нужно? И новичка не нужно никак вводить в курс дела, чтобы он как раз и не был главным героем «угадай, куда я это засунул» и не найдя, не засовывал в новое место?
Но это всё вторично. Как, если не декоратором (CommentsService с методом create(text) — то же самое, только в профиль)?
Но это всё вторично. Как, если не декоратором (CommentsService с методом create(text) — то же самое, только в профиль)?
Угу, только если код лежит в неожиданных местах, знаний архитектуры и ТЗ окажется недостаточно, в случае чего придётся лопатить весь связанный код, и не только новичку, но и старичку, особенно, если данный код писал его изобретательный коллега )
Первое место для поиска — модель и callback на create, конечно, откуда его и вытащили. Если вытаскивать, то вместе с другим кодом, например, в делегат. Можно оставить и в декораторе, только вызывать из модели, а не контроллера. То что тяжёлые коллбеки делают тесты медленнее и более хрупкими, конечно, понятно, но я очень далёк от мысли, что это причина для изменений в архитектуре.
Первое место для поиска — модель и callback на create, конечно, откуда его и вытащили. Если вытаскивать, то вместе с другим кодом, например, в делегат. Можно оставить и в декораторе, только вызывать из модели, а не контроллера. То что тяжёлые коллбеки делают тесты медленнее и более хрупкими, конечно, понятно, но я очень далёк от мысли, что это причина для изменений в архитектуре.
Если у модели наличествует большой и тяжелый callback, я обычно завожу в модели виртуальный атрибут, истинность которого отменяет callback. В обычной работе он совершенно не мешает, а в тестах позволяет сэкономить время и сохранить чистоту.
Сразу оговорюсь: я тоже за, что вызов декоратора должен быть не в контроллере.
Декоратор красив только в данном сферическом примере в вакууме. Первый «неудобный» случай уже описан выше — это когда нужно создать комментарий где-то еще. Приходится заново прописывать конструкцию с декоратором, а это нарушение DRY.
Второй случай — это когда нужно сделать больше одного действия. Например, нужно отправить коммент в FB, Twitter, отправить оповещение автору статьи на почту, да еще и начислить какие-нибудь баллы. Будете запихивать это все в один декоратор? Или строить монструозную цепочку из декораторов при каждом создании комментария? За оба эти решения вы через месяц проклянете сами себя. Особенно, если вам таки нужно создавать комментарии где-то еще. Создание нескольких обсерверов, обслуживающих одну модель, но выполняющих разную работу, выглядит гораздо красивее.
Третий случай — это тестирование. В случае с обсервером вам нужно протестировать обсервер — и все. Обсервер будет отрабатывать всегда, когда создается комментарий. В случае с декоратором вам придется проверять каждый случай на предмет того, не забыли ли вы, ваш коллега или новичок в кофейном угаре использовать декоратор.
Второй случай — это когда нужно сделать больше одного действия. Например, нужно отправить коммент в FB, Twitter, отправить оповещение автору статьи на почту, да еще и начислить какие-нибудь баллы. Будете запихивать это все в один декоратор? Или строить монструозную цепочку из декораторов при каждом создании комментария? За оба эти решения вы через месяц проклянете сами себя. Особенно, если вам таки нужно создавать комментарии где-то еще. Создание нескольких обсерверов, обслуживающих одну модель, но выполняющих разную работу, выглядит гораздо красивее.
Третий случай — это тестирование. В случае с обсервером вам нужно протестировать обсервер — и все. Обсервер будет отрабатывать всегда, когда создается комментарий. В случае с декоратором вам придется проверять каждый случай на предмет того, не забыли ли вы, ваш коллега или новичок в кофейном угаре использовать декоратор.
Ну вот вы обоснованно ругаете декоратор за «нужно создать комментарий где-то ещё». А что, если где-то ещё мне, создавая комментарий, не нужно посылать сообщение по электропочте? А тут этот Observer, о котором я, кстати, будучи новичком в проекте, ещё и не знаю. Связь то неявная.
ППКС. Автору респект. Правда, по-моему это не правила рефакторинга моделей, а правила их написания :) Какая разница, когда их применять!
Я бы почитал как автор это раскидывает по директориям. Я додумался только до /app/services, в котором хранятся корневые сервисы. А остальное куда? Всякие value object и стратегии. В lib что ли? Как-то далеко рыться.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
7 паттернов рефакторинга толстых моделей в Rails