Rubyには、モジュールという概念がある。この機能により、クラスが128倍パワーアップするという素敵なものなのである。これから解説していこうと考えているわけだが、ちょっとその前に、少しお話しておきたいことがあるのだ。
あるクラスを基にして新しいクラスを作成することを『継承』と呼ぶのであるが、実はこの『継承』には、2種類の方法がある。
それは、基になるクラスをひとつだけに限定するか、もしくは複数を許すかということなのだ。前者は『単一継承』、後者は『多重継承』と呼ばれている。
下等な生物であれば単一生殖もありうるが、通常単一では子供が生まれない。であるから、親子という概念からすると、『多重継承』の方が自然であるような気もするが、オブジェクト指向プログラミング言語においての継承は、親子関係のメタファーではなくどちらかというと『種の進化』という側面が強いので、『単一継承』も十分アリだという気もする。
しかしまあ、現実の世界になぞらえて、プログラミング言語を考えるのは、楽しいけれども、あまり意味のないことなので、言語仕様という側面から2つの継承方法を考えてみよう。
実は、数あるオブジェクト指向(っぽいものも含む)プログラミング言語で、多重継承をサポートしているものは少ないのである。Eiffel(アイフェル、エッフェル)、C++言語、Perl、Pythonぐらいのものであろうか。その他の言語は、多重継承はサーポートしていない。なぜなら、多重継承は、非常に問題点が多いのだ。
1.継承関係が複雑になりすぎる。
2.メンバ名の衝突。
3.コンパイラ、インタプリタの実装が難しくなる。
とまあ、3番目の問題は、言語開発者サイドからみた問題点であるので、使う立場の者にとっては関係ないことだが、1と2については、なかなかに問題は深刻だ。そのため、比較的新しく誕生したオブジェクト指向プログラミング言語、例えば、Objective-C、Java、C#、VisualBasic.NETなどは、多重継承をサポートしていないのである。その代わりといっては何だが、これらの言語にはInterfase(インターフェイス)という概念がある。
例えば、人間クラスと鳥クラスの両方の機能を継承して、鳥人間クラスを作りたいとする。どうしてそのようなクラスを作りたいかというと、鳥のように大空を飛び回る人間を作りたかったのだ。これが多重継承であれば話は簡単で、例えば次のようにすればよい。
001 | // 人間クラス
002 | class CHuman
003 | {
004 | };
005 | // 鳥クラス
006 | class CBard
007 | {
008 | void fly()
009 | {
010 | }
011 | };
012 | // 鳥人間クラス
013 | class CBardMan : public CHuman, public CBard
014 | {
015 | };
016 |
017 | CBardMan bm;
018 | bm.fly();
これで、大空を自由に飛べる新しい人類が誕生し、めでたしめでたしと思っていたのだが、様々な問題があることがわかってきたのである。例えば、鳥目のために夜何も見えないとか、公園の池でぴちょっと跳ねる鯉をパクッといきたいという衝動にかられるなどの問題だ。
これはやはりまずいよということで登場したのが、インターフェイスという概念なのである。
今回、人間クラスに追加したいのは、『空を飛ぶ』という機能なのである。そこで、『空を飛ぶ』という機能を仕様として切り出し、インターフェイスとして定義するのである。C#を例に、実際に定義してみよう。
001 | // 空飛ぶインターフェイス
002 | public interfase IFly
003 | {
004 | void approach(); // 助走
005 | bool takeoff(); // 離陸
006 | void flutter(); // 羽ばたき
007 | void ascend(); // 上昇
008 | void gliding(); // 滑空
009 | void descent(); // 下降
010 | bool landing(); // 着地
011 | }
そして、この空飛ぶインターフェイスを鳥クラスと、人間クラスに定義してやるのだ。
001 | class CHuman : IFly
002 | {
003 | }
004 | class CBard : IFly
005 | {
006 | }
よしこれで完璧だと安心するのはまだ早計で、実はIFlayインターフェースにある7つのメソッドは、全てガラだで、中身が詰まっていないのである。『鳥と人間とでは、同じ空を飛ぶにしたって、やることはちょっとずつ違うだろう。だから、各メソッドの中身は全部自分で実装してくれ』というスタンスなのだ。その代わり、IFlyというインターフェースを持つクラスのインスタンスは、外部から見ると全く同じ操作で空を飛ばすことができるというわけである。
継承できるクラスはたったひとつだけだが、インターフェイスは複数くっつけることができる。要するにインターフェースとは『仕様の多重継承』というわけである。
実になんともすばらしい概念なのだが、ここでひとつ不満が沸いてこないだろうか。それは、
「なんだよ。実装を共有できないのかよ」という不満である。可能であれば、マンガのドラえもんに出てくるタケコプターのように、頭にくっつければ後は勝手に飛んでくれるというのがよろしい。
そこで登場したのが、RubyのMix-in(ミックスイン)という考え方だ。これは、中身のつまったインターフェイスと考えればよろしい。それを実現するのがモジュールというわけなのである。
001 | #タケコプターモジュールの定義
002 | module TakeCopter
003 | def fly
004 | #空を飛ぶノウハウがぎっしりと・・・
005 | puts "飛んで飛んで飛んで"
006 | end
007 | end
008 |
009 | #鳥人間クラスの定義
010 | class BardMan
011 | #タケコプターモジュールのMix-in
012 | include TakeCopter
013 | end
014 |
015 | bm=BardMan.new
016 | bm.fly
12行目が、モジュールをインクルードしているところで、この一行さえあれば、タケコプターモジュールの機能を全て自分の中に取り込むことができる。16行目で、flyメソッドが、さも自分のもののように呼び出されているのを見ていただきたい。
上記のコードは、コード作成時、静的にモジュールを取り込んだわけだが、プログラム実行中、動的に取り込むこともできる。
001 | #タケコプターモジュールの定義
002 | module TakeCopter
003 | def fly
004 | #空を飛ぶノウハウがぎっしりと・・・
005 | puts "飛んで飛んで飛んで"
006 | end
007 | end
008 |
009 | #鳥人間クラスの定義
010 | class BardMan
011 | end
012 |
013 | bm=BardMan.new
014 | #タケコプターモジュールを動的に取り込む
015 | bm.extend TakeCopter
016 | bm.fly
もう、ここまでくると、もうなんでもありなのかという気持ちになってくる。これだけ強力な機能があれば、もしかするとクラスの継承すら、もう必要ないのかもしれない。
最終更新:2008年12月12日 16:48