модуль 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–2021 David Heinemeier Hansson
Licensed under the MIT License.