2nd
[ruby][howto]クラスを生成するクラスの作り方
Rubyな話。
「とあるオブジェクト群はそのなかでクラスタに分かれていて、クラスタごとに振る舞いが違うんだけど、そのクラスタの数が不定」という状況がたまぁ〜にある。Ruby なおまえらがよく知っている例としては Active Record パターンとかはそう。Active Record パターンだとオブジェクトは DB の行で、したがって振る舞いは DB のテーブル(かビュー)によって決まるんだけど、Active Record の基底クラスを設計している段階ではどんなテーブルがあるかなんてのは当然すべてのパターンを網羅的に作成しておく事はできない。
んで、Rails についてくる ActiveRecord::Base だと、そのへんはかつては「クラスが継承されたときにそこにフックして派生クラスに実装を注入」というインド人もびっくりの力技で解決されていて(今見たら今はそこまでじゃないが)、まあそれでみんな不満なくお使いですので特に申し上げることはございませんが、世の中にはもう少しエレガントな解決方法もありえて、つまりクラスを明示的に生成するメソッドがあればいい。そういう例はたとえば Ruby には Struct というクラスがあって、 Struct.new は Struct のインスタンスではなくて Struct の派生クラスを返す。
で、そのようなメソッドをどう作るかというと、これはイディオムがあって
class Foo
def self.foo
Class.new self
end
end
はい、これ試験に出ますよ(メタプログラミング検定3級)。ただし Struct.new みたいに new で新しいクラスを作成しようと思うとちょっとひねらないといけない。なんでかというと
class Foo
def self.new
Class.new self
end
end
だとインスタンスを作成するための new がつぶれちゃうから。これを回避するには、
class Foo
def self.new
Class.new self do
class << self
define_method :new, Class.instance_method(:new)
end
end
end
end
こんな風にする。Class の instance method の new とはようするに、普通の new だ。
というわけで今回は特に毒があるエントリではないけど、この件は見た感じレシピ系の本にはどれにも載ってなさそうっぽいので、書いておくことにした。おまえらは知っておいて損はないよ。