無名クラスを継承する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の機能は、その必要性が明らかになるまですべて無視するというものだ。」とあります。(なんじゃそりゃ、笑
なので、頭の片隅において、動的に書いたほうがすっきりしそうな場面に当たった時に引き出しから出せば良いかと。
- - 関連記事 -
- プロキシオブジェクトの例で関数型プログラミングを理解
- method_missing()とsend()でフック(処理を捕捉)
- obj.instance_eval(&block)とblock.call(obj)の違いを確認
- コードブロックを用いたサーバーとクライアントの例
- メソッド名とシグニチャを統一して美しいAPI設計
- mustメソッドで読みやすいテスト