オープンクラスってすごいことなのか
オープンクラスってすごいことなのかなと思ってググッてみた。Juixe Techknowというブログの、この解説が参考になった。
やっぱりオープンクラスはふつうのことではないか、少なくともJavaとは非常に好対照となっているようだ。「Rubyではクラスを閉じることがない」という表現が出てくる。クラスって閉じるものなの? 既存クラスにメソッドを追加するときに「クラスをreopneする」と言ってるけど、そんなに大げさなものだったのか……。Javaが「固い言語」と言われてるのはそのへんのことなだろうか。
JavaのStringクラスは物足りないらしく、いろいろと機能を追加するためのライブラリが存在するという。例えばApacheのJakarta Comons LangプロジェクトではStringUtilsというStiringヘルパークラスがある。IsEmptyとかChomp/Chopみたいなメソッドを提供する。しかし、そうした機能を使うためにはライブラリをダウンロードし、設定し、使い方を学び、ライブラリをインポートし、書き方(呼び方)も標準のStringとは違うやり方で行う必要がある。これに対して、RubyではビルトインのStringクラスにいきなりメソッドを追加できる。だから「Ruby rocks!」ということらしい。
しかし、いいメソッド群を提供するコードがあったとしても、どっちみちインポートしないといけないのとか、使い方を学ばないといけないのはJavaもRuby も同じじゃないんだろうか。JavaのJCPによる標準化プロセスが遅くて、標準クラスにメソッドを提案しても採用されるには年単位で時間かかる、ということもRubyのオープンクラスと比べたときのデメリットのように指摘しているけど、それってクラスのアーキテクチャ比較じゃなくて仕様の決定、標準化プロセスの比較に思える。
で、既存クラスにメソッドを追加するには2つの方法があって、1つはふつうにしれっとclass宣言でやるやりかた。もう1つはobj.class_evalというメソッドを使うやりかた。こんな感じらしい。
String.class_eval do def length 10 end end
これって何の意味があるんだろうかと思ったけど、class_evalメソッドは、どんなクラスに対しても定義されているようだ。だからオブジェクトを引数にして、そのオブジェクトが属するクラスに対してメソッドを定義する次のような関数が作れる。
def add_method(obj) obj.class.class_eval do def to_string to_s end end end
実行結果は、こんな感じ。
irb(main):008:0> 1.to_s => "1" irb(main):009:0> 1.to_string NoMethodError: undefined method `to_string' for 1:Fixnum from (irb):9 from :0 irb(main):010:0> add_method(10) => nil irb(main):011:0> 1.to_string => "1"
メソッドを定義するクラスを指定するために適当なオブジェクトを渡すのが気持ち悪い。なんか方法があるんだろうか。
【追記】方法は以下。
def add_method(klass) klass.class_eval do def to_string to_s end end end add_method(Fixnum)
Rubyのオープンクラスという仕様は大規模開発には向かないと聞いたことがある。そのへんの議論は、例えばAre Ruby's Open Classes a Poor Fit for Large Projects?にある。典型的な議論としてはやっぱり運用期間が長く、開発者メンバーも多いプロジェクトではダイナミックにメソッドが変わるのはメリットよりデメリットのほうが多いのではないか、というもの。これに対するRubyistの典型的な回答は、「そう思うならその機能は使うな」というものと「ユニットテスト書けよ」というものらしい。なるほどなぁ。
大規模開発にRubyは向かないということに対する反論として、いやいやObjective-CだってSmalltalkだってオープンクラスだけど、充分うまくっているエンタープライズクラスのプロジェクトだってあるよ、RubyがダメだなんていうのはC++やJavaやってる頭の固い連中の寝言じゃねぇか、というような典型的なフレームの火種も興味深い。そもそも「大規模って何だよ」ということもRuby擁護派は言っている。JavaやC#に比べてコード量が3分の2とか10分の1にになるんだぜ、とか。ほんまかいな。