Spec-Zone.ru › Ruby on Rails 4.2

модуль 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 для примитивной модульности типа «это внутренняя информация».

Методы публичного экземпляра

concern(topic, &module_definition) Показать исходный код
# 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
concerning(topic, &block) Показать исходный код
# 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.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API