第2章 火曜日メソッド

Ruby東海 Wiki - 第15回勉強会で司会を務めるので、そのメモ

  • Javaなどのコンパイラによる静的型チェックが行われる静的言語に対し、PythonRubyのような動的言語ではそのようなことは行われない
  • 動的言語では存在しないメソッドを呼び出すようなコードを書いても実行されるまでは問題にならない。そういう特性をつかった技術をこの章では紹介する

2.1重複問題

コンピュータ用品管理システムを作る例題。99ドルよりも高い経費を自動的に検出するようにする。データソースはあらかじめ用意されているので、それを使ってレポートを作成するコードを書いていく

  • 2.1.2でコードを書くがそれぞれのメソッドはパーツ名が違うだけでほとんど同じ
  • 動的メソッド(動的ディスパッチ)かmethod_missing()を使えば解決
  • メソッドを動的に呼び出すにはObject#sendメソッドを使えばよい
  • (補足)シンボルはイミュータブル・軽量
  • 例としてCampingのYAMLのコピー・Test::Unitを紹介
method_names = public_instance_methods(true)
tests_method_names.delete_if {|method_name| method_name !~ /^test./}

パブリックなインスタンスメソッドを取得して正規表現で必要なメソッドだけ取得。動的!これをパターンディスパッチとも言う

  • Object#sendはプライベートメソッドも呼ばれてしまう
  • (補足)instance_eval()に渡したメソッドをコンテキスト探査機という
  • メソッドを動的に定義するのにModule#define_method()がある
  • 手順1で動的ディスパッチを追加して同じような名前のメソッドをまとめる
  • 手順2でdefine_method()を使ってメソッド定義をまとめる
  • クラスメソッドはクラスの定義で実行される
  • 手順3ではイントロスペクション(リフレクション)でデータソースのメソッドをgrepして完全に重複コードを削除した

2.3 method_missing()

  • 2.3.0 メソッドが見つからない場合は、Rubyは元のレシーバオブジェクトのmethod_missing()を呼び出す
  • method_missing()はBasicObjectのインスタンスメソッドなのですべてのオブジェクトが所有している
  • 2.3.1 method_missing()をオーバーライドすることもできる
  • 2.3.2 method_missing()で処理されたメッセージは、そのメソッド定義がコード中にないためゴーストメソッドと呼ばれる
  • Ruportというライブラリではto_??やrows_with_??というメソッドで活用している
  • OpenStrucという属性を代入するだけでつくれるクラスがある。これもmethods_missing()を使っている-2.3.3 動的プロキシ
  • 2.3.4 method_missing()を使ってリファクタリング
  • respond_to?をオーバーライドしておくと

2.4 クイズ:バグ退治

method_missing()のバグと見つけるのは大変という例題

2.5もっとmethod_missing()

  • メソッド名が衝突することがある
  • Module#undef_method()で削除、Module#remove_method()で継承したメソッド以外を削除
  • パフォーマンスは落ちる(例では2倍)
  • 消したらまずいメソッドもある(__send__(),__id__()など)
  • 1.9からはブランクスレートが言語に組み込まれた(BasicObject)

まとめ

  • 動的メソッドや動的ディスパッチで重複コードを効率的に省くことができる
  • 動的プロキシとブランクスレートを使う方法もある