Ruby Language
メッセージの受け渡し
サーチ…
前書き
オブジェクト指向設計では 、オブジェクトはメッセージを受信し、メッセージに応答します。 Rubyでは、メッセージの送信はメソッドの呼び出しであり、そのメソッドの結果は応答です。
Rubyでは、メッセージの受け渡しは動的です。メッセージがどのように返信するかを正確に知るのではなく、メッセージが到着したとき、Rubyは事前定義されたルールセットを使用して返信できるメソッドを見つけます。これらのルールを使用して、メッセージに割り込んだり応答したり、別のオブジェクトに送信したり、他のアクションの中で変更したりすることができます。
オブジェクトがメッセージを受け取るたびに、Rubyは以下をチェックします:
- このオブジェクトにシングルトンクラスがあり、このメッセージに返信できる場合。
- このオブジェクトのクラスを調べ、クラスの先祖のチェーンを探します。
- この祖先でメソッドが使用可能であるかどうかを1つずつチェックし、チェーンを上に移動します。
継承チェーンを通るメッセージ
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
SubExample.ancestors # => [SubExample, Example, Object, Kernel, BasicObject]
SubExample
からSubExample
ます。 subexample_method
メッセージを送信すると、Rubyは利用可能な1つのSubExampleを選択し、 Example#subexample_method
を無視します。
s.subexample_method # => :subexample
後SubExample
それはチェックExample
。私たちは送ればexample_method
RubyがあればチェックしSubExample
それに返信したりすることはできませんし、それが可能であるためではないRubyはチェーンを上がると覗くExample
。
s.example_method # => :example
Rubyが定義されたすべてのメソッドをチェックした後、それが応答できるかどうかをmethod_missing
ためにmethod_missing
を実行します。私たちは送ればmissing_subexample_method
Rubyは上で定義された方法を見つけることができませんSubExample
それが上に移動してExample
。 Example
やチェーン上の他のクラスでも定義されたメソッドを見つけることはできません。 Rubyはやり直して、 method_missing
を実行しmethod_missing
。 method_missing
のSubExample
に返信できmissing_subexample_method
。
s.missing_subexample_method # => :subexample
しかし、メソッドが定義されている場合、たとえそれがチェーン内で上位であっても、Rubyは定義されたバージョンを使用します。私たちが送信した場合たとえば、 not_missed_method
にもかかわらず、 method_missing
のSubExample
それに答えることができRubyが上に歩くSubExample
それはその名前の定義されたメソッドを持っていないためとに見えるExample
1を有しています。
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
メッセージを中断する
メッセージを中断する方法は2つあります。
- 定義されていないメッセージには、
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
呼び出す前にそれらのデータを分割しなければならないとしましょう。 SubExample
initialize
を定義することができます。
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