Ruby Language
Meddelande vidarebefordras
Sök…
Introduktion
I Objektorienterad design, föremål ta emot meddelanden och svara på dem. I Ruby kallar man en metod och att resultatet av den metoden är svaret.
I Ruby är meddelandet vidarebefordran dynamiskt. När ett meddelande kommer snarare än att veta exakt hur man svarar på det använder Ruby en fördefinierad uppsättning regler för att hitta en metod som kan svara på den. Vi kan använda dessa regler för att avbryta och svara på meddelandet, skicka det till ett annat objekt eller ändra det bland andra åtgärder.
Varje gång ett objekt får ett meddelande kontrollerar Ruby:
- Om detta objekt har en singleton-klass och det kan svara på det här meddelandet.
- Slå upp objektets klass sedan klassens förfäder kedja.
- En efter en kontrollerar om en metod är tillgänglig på denna förfader och flyttar uppåt kedjan.
Meddelande som passerar genom arvkedjan
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
För att hitta en lämplig metod för SubExample#subexample_method
Ruby tittar först på förfädernas kedja till SubExample
SubExample.ancestors # => [SubExample, Example, Object, Kernel, BasicObject]
Det börjar från SubExample
. Om vi skickar meddelandet subexample_method
väljer Ruby det tillgängliga underexemplet och ignorerar Example#subexample_method
.
s.subexample_method # => :subexample
Efter SubExample
kontrollerar det Example
. Om vi skickar example_method
Ruby kontrollerar om SubExample
kan svara på det eller inte och eftersom det inte kan Ruby går upp kedjan och tittar i Example
.
s.example_method # => :example
Efter att Ruby har kontrollerat alla definierade metoder körs method_missing
att se om den kan svara eller inte. Om vi skickar missing_subexample_method
Ruby inte hitta en definierad metod på SubExample
så att den går upp till Example
. Den kan inte hitta en definierad metod i Example
eller någon annan klass högre i kedjan heller. Ruby börjar om och kör method_missing
. method_missing
of SubExample
kan svara på missing_subexample_method
.
s.missing_subexample_method # => :subexample
Men om en metod är definierad använder Ruby en definierad version även om den är högre i kedjan. Om vi till exempel skickar not_missed_method
även om method_missing
av SubExample
kan svara på det Ruby går upp på SubExample
eftersom det inte har en definierad metod med det namnet och tittar på Example
som har en.
s.not_missed_method # => :example
Meddelande som passerar modulens sammansättning
Ruby rör sig upp på förfädernas kedja av ett objekt. Denna kedja kan innehålla både moduler och klasser. Samma regler för att flytta upp kedjan gäller också för moduler.
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
Avbryta meddelanden
Det finns två sätt att avbryta meddelanden.
- Använd
method_missing
att avbryta alla icke definierade meddelanden. - Definiera en metod i mitten av en kedja för att fånga meddelandet
Efter avbrott i meddelanden är det möjligt att:
- Svara på dem.
- Skicka dem någon annanstans.
- Ändra meddelandet eller resultatet.
method_missing
via method_missing
och svara på meddelande:
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
Avlyssna meddelandet och ändra det:
class Example
def initialize title, body
end
end
class SubExample < Example
end
Låt oss nu föreställa oss att våra data är "titel: body" och vi måste dela dem innan vi kallar Example
. Vi kan definiera initialize
på SubExample
.
class SubExample < Example
def initialize raw_data
processed_data = raw_data.split ":"
super processed_data[0], processed_data[1]
end
end
Avlyssna meddelandet och skicka det till ett annat objekt:
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