Szukaj…


Wprowadzenie

W projektowaniu obiektowym obiekty odbierają wiadomości i odpowiadają na nie. W języku Ruby wysyłanie wiadomości wywołuje metodę, a wynikiem tej metody jest odpowiedź.

W Ruby przekazywanie wiadomości jest dynamiczne. Gdy wiadomość dotrze, zamiast dokładnie wiedzieć, jak na nią odpowiedzieć, Ruby używa predefiniowanego zestawu reguł, aby znaleźć metodę, która może na nią odpowiedzieć. Możemy użyć tych reguł, aby przerwać i odpowiedzieć na wiadomość, wysłać ją do innego obiektu lub zmodyfikować między innymi akcjami.

Za każdym razem, gdy obiekt otrzymuje komunikat Ruby sprawdza:

  1. Jeśli ten obiekt ma klasę singleton i może odpowiedzieć na tę wiadomość.
  2. Sprawdza klasę tego obiektu, a następnie łańcuch przodków klasy.
  3. Jeden po drugim sprawdza, czy metoda jest dostępna dla tego przodka i przesuwa się w górę łańcucha.

Wiadomość przekazywana przez łańcuch dziedziczenia

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

Aby znaleźć odpowiednią metodę dla SubExample#subexample_method Ruby najpierw sprawdza łańcuch przodków SubExample

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

Zaczyna się od SubExample . Jeśli wyślemy wiadomość z subexample_method Ruby wybierze jedną z dostępnych SubExample i zignoruje Example#subexample_method .

s.subexample_method # => :subexample

Po SubExample sprawdza Example . Jeśli wyślemy przykładową example_method Ruby sprawdza, czy SubExample może na nią odpowiedzieć, czy nie, a ponieważ nie może, Ruby idzie w górę łańcucha i patrzy na Example .

s.example_method # => :example

Po tym, jak Ruby sprawdzi wszystkie zdefiniowane metody, uruchamia brak method_missing aby sprawdzić, czy może odpowiedzieć. Jeśli mamy wysłać missing_subexample_method Ruby nie będzie w stanie znaleźć określoną metodą na SubExample więc przenosi się do Example . Nie może również znaleźć zdefiniowanej metody w Example ani żadnej innej klasie wyższej w łańcuchu. Ruby zaczyna od nowa i uruchamia brak method_missing . method_missing z SubExample może odpowiedzieć missing_subexample_method .

s.missing_subexample_method # => :subexample

Jednak jeśli metoda jest zdefiniowana, Ruby używa zdefiniowanej wersji, nawet jeśli jest wyższa w łańcuchu. Na przykład, jeśli wyślemy not_missed_method nawet jeśli method_missing SubExample może na nie odpowiedzieć, Ruby podchodzi do SubExample ponieważ nie ma zdefiniowanej metody o takiej nazwie i sprawdza Example który ją posiada.

s.not_missed_method # => :example

Przekazywanie wiadomości przez skład modułu

Ruby przesuwa się w górę łańcucha przodków obiektu. Ten łańcuch może zawierać zarówno moduły, jak i klasy. Te same zasady dotyczące awansowania łańcucha dotyczą również modułów.

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

Przerywanie wiadomości

Istnieją dwa sposoby przerywania wiadomości.

  • Użyj method_missing aby przerwać każdą niezdefiniowaną wiadomość.
  • Zdefiniuj metodę w środku łańcucha, aby przechwycić wiadomość

Po przerwaniu wiadomości można:

  • Odpowiedz im.
  • Wyślij je gdzieś indziej.
  • Zmodyfikuj wiadomość lub jej wynik.

Przerwanie przez brak method_missing i odpowiedź na wiadomość:

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

Przechwytywanie wiadomości i modyfikowanie jej:

class Example
  def initialize title, body
  end
end

class SubExample < Example
end

Teraz wyobraźmy sobie, że nasze dane to „tytuł: ciało” i musimy je rozdzielić przed wywołaniem Example . Możemy zdefiniować initialize na SubExample .

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

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

Przechwytywanie wiadomości i wysyłanie jej do innego obiektu:

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
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow