Поиск…


Вступление

В объектно-ориентированном дизайне объекты получают сообщения и отвечают на них. В Ruby отправка сообщения вызывает метод, и результатом этого метода является ответ.

В Ruby передача сообщений динамическая. Когда сообщение приходит, а не точно знает, как ответить на него, Ruby использует предопределенный набор правил для поиска метода, который может ответить на него. Мы можем использовать эти правила для прерывания и ответа на сообщение, отправки его другому объекту или изменения его среди других действий.

Каждый раз, когда объект получает сообщение, Ruby проверяет:

  1. Если этот объект имеет одноэлементный класс, он может ответить на это сообщение.
  2. Выбирает класс объекта, а затем класс «предки».
  3. Один за другим проверяет, доступен ли метод этому предку и перемещается вверх по цепочке.

Сообщение, проходящее через цепочку наследования

class Example
  def example_method
    :example
  end

  def subexample_method
    :example
  end

  def not_missed_method
    :example
  end

  def method_missing name
    return :example if name == :missing_example_method
    return :example if name == :missing_subexample_method
    return :subexample if name == :not_missed_method
    super
  end
end

class SubExample < Example
  def subexample_method
    :subexample
  end

  def method_missing name
    return :subexample if name == :missing_subexample_method
    return :subexample if name == :not_missed_method
    super
  end
end

s = Subexample.new

Чтобы найти подходящий метод для SubExample#subexample_method Ruby сначала рассмотрит цепочку предков SubExample

SubExample.ancestors # => [SubExample, Example, Object, Kernel, BasicObject]

Он начинается с SubExample . Если мы отправляем сообщение subexample_method Ruby выбирает один доступный один SubExample и игнорирует Example#subexample_method .

s.subexample_method # => :subexample

После SubExample он проверяет Example . Если мы отправим SubExample example_method Ruby, если SubExample может ответить на него или нет, и поскольку он не может Ruby подниматься по цепочке и просматривает Example .

s.example_method # => :example

После того, как Ruby проверяет все определенные методы, он запускает method_missing чтобы узнать, может ли он ответить или нет. Если мы отправим missing_subexample_method Ruby не сможет найти определенный метод в SubExample чтобы он перемещался до Example . Он не может найти определенный метод на Example или любой другой класс, выше в цепочке. Ruby запускается и запускает method_missing . method_missing SubExample может ответить на missing_subexample_method .

s.missing_subexample_method # => :subexample

Однако, если метод определен, Ruby использует определенную версию, даже если она выше в цепочке. Например, если мы отправляем not_missed_method хотя method_missing SubExample может ответить на него, Ruby подходит к SubExample потому что он не имеет определенного метода с этим именем и смотрит в Example который имеет один.

s.not_missed_method # => :example

Прохождение сообщения через композицию модуля

Ruby движется вверх по цепи предков объекта. Эта цепочка может содержать как модули, так и классы. Те же правила о продвижении цепи распространяются и на модули.

class Example
end

module Prepended
  def initialize *args
    return super :default if args.empty?
    super
  end
end

module FirstIncluded
  def foo
    :first
  end
end

module SecondIncluded
  def foo
    :second
  end
end

class SubExample < Example
  prepend Prepended
  include FirstIncluded
  include SecondIncluded

  def initialize data = :subexample
    puts data
  end
end

SubExample.ancestors # => [Prepended, SubExample, SecondIncluded, FirstIncluded, Example, Object, Kernel, BasicObject]

s = SubExample.new # => :default
s.foo # => :second

Прерывание сообщений

Существует два способа прерывания сообщений.

  • Используйте method_missing для прерывания любого не определенного сообщения.
  • Определите метод в середине цепочки для перехвата сообщения

После прерывания сообщений можно:

  • Ответьте им.
  • Отправьте их в другое место.
  • Измените сообщение или его результат.

Прерывание через method_missing и ответ на сообщение:

class Example
  def foo
    @foo
  end

  def method_missing name, data
    return super unless name.to_s =~ /=$/
    name = name.to_s.sub(/=$/, "")
    instance_variable_set "@#{name}", data
  end
end

e = Example.new

e.foo = :foo
e.foo # => :foo

Перехват сообщения и его изменение:

class Example
  def initialize title, body
  end
end

class SubExample < Example
end

Теперь давайте представим, что наши данные «title: body», и мы должны разбить их перед вызовом « Example . Мы можем определить initialize в SubExample .

class SubExample < Example
  def initialize raw_data
    processed_data = raw_data.split ":"

    super processed_data[0], processed_data[1]
  end
end

Перехват сообщения и отправка его другому объекту:

class ObscureLogicProcessor
  def process data
    :ok
  end
end

class NormalLogicProcessor
  def process data
    :not_ok
  end
end

class WrapperProcessor < NormalLogicProcessor
  def process data
    return ObscureLogicProcessor.new.process data if data.obscure?

    super
  end
end


Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow