модуль Module::Concerning
Разделение забот на небольшие части
Часто мы сталкиваемся с блоком поведения средней величины, который хотели бы извлечь, но только смешивая его в один класс.
Извлечение обычного объекта Ruby для инкапсуляции и взаимодействия или делегирования исходному объекту часто является хорошим выбором, но когда нет дополнительных состояний для инкапсуляции или мы создаём объявления в стиле DSL для родительского класса, введение новых взаимодействий может сбить с толку, а не упростить.
Типичный путь — просто вывалить всё в монолитный класс, возможно, с комментарием, как наименее плохой вариант. Использование модулей в отдельных файлах означает утомительное перебирание, чтобы получить общее представление.
Неудовлетворительные способы разделения мелких забот
Использование комментариев:
class Todo < ApplicationRecord
# Other todo implementation
# ...
## Event tracking
has_many :events
before_create :track_creation
private
def track_creation
# ...
end
end
С встроенным модулем:
Шумный синтаксис.
class Todo < ApplicationRecord
# Other todo implementation
# ...
module EventTracking
extend ActiveSupport::Concern
included do
has_many :events
before_create :track_creation
end
private
def track_creation
# ...
end
end
include EventTracking
end
Шум миксов, сосланный в свой собственный файл:
Когда наш фрагмент поведения начинает приближаться к границе прокрутки для понимания, мы сдаёмся и переносим его в отдельный файл. При таком размере увеличенные накладные расходы могут быть приемлемой компенсацией, даже если это снизит наше мгновенное понимание того, как всё работает.
class Todo < ApplicationRecord # Other todo implementation # ... include TodoEventTracking end
Представление Module#concerning
Успокоив шум миксов, мы получаем естественный и простой способ разделения забот на небольшие части.
class Todo < ApplicationRecord
# Other todo implementation
# ...
concerning :EventTracking do
included do
has_many :events
before_create :track_creation
end
private
def track_creation
# ...
end
end
end
Todo.ancestors
# => [Todo, Todo::EventTracking, ApplicationRecord, Object]
Этот небольшой шаг имеет замечательные последствия. Мы можем
-
понять поведение нашего класса с одного взгляда,
-
очистить монолитные классы-погремушки, разделяя их заботы, и
-
перестать полагаться на protected/private для грубой модульности «это внутренние вещи».
Префикс concerning
concerning поддерживает аргумент prepend: true, который prepend заботу вместо использования include.
Методы публичного экземпляра
# File activesupport/lib/active_support/core_ext/module/concerning.rb, line 132
def concern(topic, &module_definition)
const_set topic, Module.new {
extend ::ActiveSupport::Concern
module_eval(&module_definition)
}
end Короткое решение для определения заботы.
concern :EventTracking do ... end
эквивалентно
module EventTracking extend ActiveSupport::Concern ... end
# File activesupport/lib/active_support/core_ext/module/concerning.rb, line 114 def concerning(topic, prepend: false, &block) method = prepend ? :prepend : :include __send__(method, concern(topic, &block)) end
Определите новую заботу и примените её.
© 2004–2020 David Heinemeier Hansson
Licensed under the MIT License.