このようにメリットがあるオブジェクト指向だが,なかなか定着しなかったのも事実である。その理由として一番言われてきたのが,前述の発想の転換を強いることである。構造化プログラミングに慣れたプログラマは,基本的に「ロジックをどうするか」を考える。それに対し,オブジェクト指向では「データをどうするか」を考えなければならない。ここで発想の転換が必要である。

 ところがその一方で,オブジェクト指向でもプログラミング作業は最終的に,「どういう手順で何をするか」を記述しなければならない。メソッドの定義である。つまり,オブジェクト指向的な発想だけではプログラミング作業は完了しないのである。どこかでまたアタマを切り替えなければならないのだ注7)。だからどうしても,手順を中心に考える発想から抜け出せないのだ。この問題はプログラミング言語も追い打ちをかけてしまう。特にオブジェクト指向言語として一番最初に普及したC++は,C言語のプリプロセサとして実装されていたため,C言語コンパイラとしても問題なく使えてしまう。

 もう一つ混乱しやすいのは,データを考える段階ではデータそのものに着目していても,それを実装する段階ではデータの容器である「クラス」を定義することにもあるだろう。手続き型の世界では,データとして利用する変数など,基本的に実体である。たまにひとかたまりで使いたいデータの組があれば,構造体を定義するという感覚だ。これに対しオブジェクト指向の場合はまず入れ物を考えなければならない。データを使う場合に片っ端からオブジェクトにすることを考えればいいのかもしれないが,ある程度きちんと設計していないとなかなかうまくオブジェクトを抽出できない。特にGUI設計を中心とした開発ツールを使っていると,小さなプログラムの場合あらかじめ設計などをせず,ついついイベント・ハンドラに手続きを書いてしまうことになりがちである注8)

意外とやっかいな多重継承問題

 オブジェクト指向でやっかいな問題の一つが,継承の問題である。抽出したオブジェクトを関連付けて体系的に整理する作業は,決して楽なものではない。1~2階層程度ならばまだしも,階層が増え複雑さを増すと「よい整理方法」になっているかどうかわからなくなる。このことは,適切なオブジェクトの抽出につながる問題でもある。

 もう一つやっかいなのが,多重継承である。多重継承はC++やEiffelといったオブジェクト指向言語が取り入れているものだ(図6[拡大表示])。例えば生物で言えば,カモノハシはほ乳類に属するが,卵を産むという性質もある。この場合,生物学分類としての性質と,卵を産むという性質を多重継承している。現実に近いモデルを作る場合に,多重継承の考え方は有効である。

 しかし多重継承にはいくつか問題も指摘されている。一つが名称の衝突。どちらかの名称を変更したり再定義するなどにより解決することになる。ただそれ以前の問題として完全に独立した関係にある分類体系でないと,多重継承は後々混乱を生む原因となる。だから多重継承はあまり使われていないのだ。

 多重継承を有効に使う手法として提案されたのが「Mix-in」である。Mix-inでは,大きく分類としての意味的な階層と,分類関係にない別のクラスが持つ類似のコードを混ぜることである。つまり一つの継承に関しては結構複雑で意味的な構造を持つが,もう一つの継承に関しては実装にしか関心がないのである。例えば「イベントを処理する機能を持つボタン・オブジェクト」は,意味的な階層においてGUI部品のクラスを継承して作られる。このときに,イベントを処理する機能を別のクラスからユーティリティとして継承することで,新しいクラスを定義できることになる(図7[拡大表示])。このMix-inの機能を明示的に備えたプログラミング言語がRubyである。

図6●多重継承の考え方
互いに独立した整理体系から,複数の性質を継承することを多重継承という。EiffelやC++,CLOS(Common Lisp Object System)などが多重継承に対応している。一方JavaやSmalltalkは単一継承しか許していない。柔軟にモデルを作成できるという点では多重継承が求められるが,そのことが後々混乱の元にもなる。それぞれの整理体系が明示的に分けられるかどうかがカギを握ることになる。
 
図7●Mix-inの考え方

Javaのinterfaceはコピーを強要する

 ではSmalltalkやJavaのような単一継承しか許さない場合はどうなるのだろうか。Smalltalkは変数そのものは型を持たず,その変数に入ったオブジェクトによって動的に性質が決まる。したがってあらかじめその仕様(メソッド名など)が共通であることをわかるようにしておく必要がない。また実行時に動的にメソッドを付け替えることも可能である。これらのことからSmalltalkにおいては多重継承は不要とされている。

 だがJavaは静的に型付けされた,堅い型システムの言語なので,何らかの仕組みが必要である。Javaはそこに「interface」というものを追加して対応した。先の例でイベント処理の性質を実装しなければならないものとして規定することによって,複数の性質を取り込めるようにしている。ただしinterfaceには明らかな問題が一つある。個別の実装が可能なので,同じメソッド名が付いていても本当に同じ動作をするのか保証の限りではないことだ。また逆に同じ動作をさせるためにコピー・アンド・ペーストを強要することも問題である。このことが後々のバグの遠因となる可能性がある。

「継承をうまく使って,適切なクラスを定義できるようになったら本当に一人前だね。さらに多重継承を駆使したり,再利用を考慮したクラス設計ができたりしたら,もう完璧だ」
「えー。再利用なんて難しいんでしょう。道は遠いなあ」
「まあ最初は地道に。今あるフレームワークからクラスを継承させてもらうところから始めないと。その次に,これをクラスにすべき,というのが見えるだけでも十分だと思うよ」
「はーい。しばらくは先輩の作ったクラスを使わせてもらいます」

(北郷 達郎、八木 玲子)