модуль Module::Concerning
Разделение мелких областей ответственности
Часто мы сталкиваемся с достаточно крупным фрагментом поведения, который хотелось бы выделить, но только в рамках одного класса.
Часто хорошим выбором является создание обычного объекта Ruby для инкапсуляции и взаимодействия (или делегирования) с исходным объектом, но когда нет дополнительного состояния для инкапсуляции или мы создаём декларации в стиле DSL для родительского класса, введение новых коллабораторов может скорее запутать, чем упростить.
Типичным решением является просто добавление всего в монолитный класс, возможно с комментарием, как наименее худший вариант. Использование модулей в отдельных файлах означает утомительный поиск, чтобы получить общее представление.
Неудовлетворительные способы разделения мелких областей ответственности
Использование комментариев:
class Todo
# Other todo implementation
# ...
## Event tracking
has_many :events
before_create :track_creation
after_destroy :track_deletion
private
def track_creation
# ...
end
end
С встроенным модулем:
Шумный синтаксис.
class Todo
# Other todo implementation
# ...
module EventTracking
extend ActiveSupport::Concern
included do
has_many :events
before_create :track_creation
after_destroy :track_deletion
end
private
def track_creation
# ...
end
end
include EventTracking
end
Шум миксина, изолированный в отдельном файле:
Когда фрагмент нашего поведения начинает требовать прокрутки для понимания его границ, мы сдаёмся и переносим его в отдельный файл. В этом случае накладные расходы кажутся пропорциональными объёму извлечения, несмотря на размывание нашего быстрого понимания, как всё на самом деле работает.
class Todo # Other todo implementation # ... include TodoEventTracking end
Введение #concerning
Успокаивая шум миксинов, мы получаем естественный и простой способ разделения мелких областей ответственности.
class Todo
# Other todo implementation
# ...
concerning :EventTracking do
included do
has_many :events
before_create :track_creation
after_destroy :track_deletion
end
private
def track_creation
# ...
end
end
end
Todo.ancestors
# => Todo, Todo::EventTracking, Object
Этот небольшой шаг имеет замечательные последствия. Мы можем
-
быстро понять поведение нашего класса с первого взгляда,
-
очистить монолитные классы, содержащие всё, разделяя их области ответственности, и
-
перестать полагаться на protected/private для примитивной модульности типа «это внутренняя информация».
Методы публичного экземпляра
# File activesupport/lib/active_support/core_ext/module/concerning.rb, line 127
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 110 def concerning(topic, &block) include concern(topic, &block) end
Определение новой области ответственности и её добавление.
© 2004–2018 David Heinemeier Hansson
Licensed under the MIT License.