無名クラスを継承するRubyの動的機能

スポンサーリンク
スポンサーリンク
ライフスタイル関連のコンテンツ
お金 | 仕事 | 勉強 | プライベート | 健康 |
プログラミング関連のコンテンツ
C言語/C++入門 | Ruby入門 | Python入門 | プログラミング全般

=追記(2010/07/23)
Rubyベストプラクティスのひとり読書。理解が難しかった部分を中心にまとめています。
書籍から引用しすぎと思われたページ、理解したページなどを削除して、当初より公開ページを減らしました。すいません。
読書のまとめ記事は難しいと感じたので、今後はブログで読書のまとめは書かないでおきます。差し障りがあれば現存するページも削除致しますので予めご了承下さい。
=追記ここまで

スポンサーリンク

P78からの「動的な機能をつかいこなす」の章の後半部分。
リフレクション、フックなどの機能を使った説明がありけっこう難しいですが、読めば理解できる短めのサンプルコードが多いです。流し気味に。

オブジェクトごとの振る舞いを実装する

特異クラス
singleton = class << user; self; end で、userオブジェクトの特異クラスを取り出せる define_method()にはクロージャとなるブロックを渡せる(スコープのローカル変数にアクセス出来る) define_method()は特異クラス上ではプライベートなので、send()経由でアクセスする 既存のコードを拡張、変更する

Rubyはオープンクラス
instance_methods を使うとメソッドが定義済みかどうかチェックできる
alias_method => 別名+コピー
a.extend(b) => aにbの機能を継承する

クラスとモジュールをプログラムで作る

p93からのサンプルコードを参考に、以下のコードを書いてみましたが、こんなコードが動くとはRubyすごい。

# 無名のクラスを返すメソッドGreeting
def Greeting(time)
  case time
  when "morning"
    Class.new do
      def greet
        "Good morning !"
      end
    end
  when "daytime"
    Class.new do
      def greet
        "Good afternoon !"
      end
    end
  when "night"
    Class.new do
      def greet
        "Good evening !"
      end
    end
  else
    Class.new do
      def greet
        "Hello !"
      end
    end
  end
end
 
# 無名のクラスを返すメソッドを呼び出し継承できる
class Mike < Greeting "daytime"
  def introduce
    "I am Mike. "
  end
end
 
class Rinda < Greeting "morning"
  def introduce
    "I am Rinda. "
  end
end
 
mike = Mike.new
print mike.introduce
puts mike.greet
 
rinda = Rinda.new
print rinda.introduce
puts rinda.greet

継承するスーパークラスにクラスを返すメソッドを書ける。
実行結果。

>ruby 93.rb
I am Mike. Good afternoon !
I am Rinda. Good morning !

続いて、p96あたりのブロックを受け取るヘルパーメソッド。

def format(name, options={}, &block)
  formats[name] = Class.new(options[:base] || Fatty::Format, &block)
end

def some_method(name, options={}, &block); hoge; end の書き方は動的なAPIを作る際のイディオムっぽい。
Class.new(MySuperClass, &block), Module.new(&block) で、クラスやモジュールを動的に匿名で生成できる

フックとコールバックを登録する

Kernel#method_added(name)
メソッドnameが追加された時に呼び出される。

Module#append_features(mod)
モジュールにselfの機能を追加する。Module#includeの実体。

Class#inherited(subclass)
クラスのサブクラスが定義された時、新しく生成されたサブクラスを引数に呼び出される。継承を捕捉する。
p102からのSimpleTestクラスの実装が面白い。

included(class_or_module)
selfがincludeされた時に対象のクラスまたはモジュー ルを引数に呼び出されます。

Object#extend(module)
引数で指定したモジュールのインスタンスメソッドをselfの特異メソッド(selfのクラスメソッド)として追加。selfを返す。

Module#include(module)
指定されたモジュールの性質(メッドや定数、クラス変数)を追加。selfを返す。

まとめ

最上位でフックし変更する場合は、エイリアスを使って安全に変更する
フックは特定のクラスやモジュールに実装できる、そしてその下位すべてを捕捉する

module A
  extend self   # => self は A
 
  def hoge
    "fuga"
  end
end

モジュールメソッド=モジュールの特異メソッド。
モジュール関数=モジュールの特異メソッドであり、モジュールの private メソッド。
extend self で、self(A)のインスタンスメソッドをself(A)の特異メソッドとして追加する。
つまり、AのインスタンスメソッドがA(moduleオブジェクトA)の特異メソッドとなる。

最後のまとめのページに・・・
「私の経験則は、こうした先進的なRubyの機能は、その必要性が明らかになるまですべて無視するというものだ。」とあります。(なんじゃそりゃ、笑
なので、頭の片隅において、動的に書いたほうがすっきりしそうな場面に当たった時に引き出しから出せば良いかと。

スポンサーリンク
 
スポンサーリンク