модуль 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.