ひとまず細かく分けることにしました。
ということで、今回は 3.3節 の特異メソッドの話のみです。
3.3節 オブジェクトごとの振る舞い
普通のオブジェクト指向プログラミング言語では振る舞いをクラスに結び付けて定義します。Rubyではクラスだけでなく、(同一クラスの特定の)オブジェクトに振る舞いを結びつけることができます。
個人的にはまだこの機能の使いどころはつかめていません。この機能を頭の片隅に置きながら Ruby プログラムをたくさん書くことでしか体得できないのだろうなぁ、と考えています。
なお、Rubyベストプラクティスには
スタブメソッドの呼び出しが、テストの実行結果全体に影響を及ぼすことがないようにしたいためだ。とテストケースの実行における例が書いてあります。
irb で見る基本的な特異メソッド
Rubyベストプラクティスの p.79 のサンプルを実行してみました:irb(main):001:0> class User; enduser には logged_in? メソッドが定義されていて、別のオブジェクト another_user では logged_in? が使えないのが分かります。
=> nil
irb(main):002:0> user = User.new
=> #<User:0xb776a770>
irb(main):003:0> def user.logged_in?
irb(main):004:1> true
irb(main):005:1> end
=> nil
irb(main):006:0> user.logged_in?
=> true
irb(main):007:0> another_user = User.new
=> #<User:0xb775715c>
irb(main):008:0> another_user.logged_in?
NoMethodError: undefined method `logged_in?' for #<User:0xb775715c>
from (irb):8
実際に動的に特異メソッドを定義する
プログラムの内部で動的に特異メソッドを定義しようと思うと "define_method(symbolname, &block_body}" を使うのがこれまでの知識ですが、irb(main):002:0> user = User.new直接だと define_method は使えません。
=> #<User:0xb773177c>
irb(main):003:0> user.define_method(:logged_in?) { true }
NoMethodError: undefined method `define_method' for #<User:0xb773177c>
from (irb):3
define_method を行うようにするには
irb(main):004:0> singleton = class << user; self; end
=> #<Class:#<User:0xb773177c>>
irb(main):006:0> singleton.send(:define_method, :logged_in?) { true }
=> #<Proc:0xb7713cb8@(irb):6>
irb(main):007:0> user.logged_in?
=> true
と記述します。
ここで使われているテクニック:
- 004 の部分( singleton = class << user; self; end )では、「クラスメソッドの定義」と同じ手法が使われています(が、詳しくは後で…)
- 006 の部分( singleton.send(...) )では、本来 private method である define_method を send メソッドを経由することで回避しています。
Ruby におけるクラスメソッドの定義
Ruby ではクラスメソッドを以下のように定義します:class A「Ruby の世界では全てがオブジェクト」というのを思い出すと、2行目の "class << self " がクラス A のオブジェクトを拡張して(特異メソッドを定義して)いる、と読むことが…できるそうです。僕にはまだもう少し時間がかかりそう。
class << self
def message
"hello, world"
end
end
end
A.message
# => "hello, world"
なので、irb:004 で行っている class << user; self; end は user オブジェクトを拡張するためのクラス(= user オブジェクトの特異メソッドを定義するための「空間(クラス)」)を生成していることになります。
後はそのオブジェクトに対して define_method してあげればいいわけです。
0 件のコメント:
コメントを投稿