菱形継承問題
菱形継承問題(ひしがたけいしょうもんだい、英: diamond problem)は、多重継承を伴うオブジェクト指向プログラミング言語において、クラス A を2つのクラス B と C が継承し、B と C の両方をクラス D が継承する際に発生するあいまいさを指す用語である。クラス D にあるメソッドが A で定義された(かつ D においてオーバーライドされていない)メソッドを呼び出すとしたとき、B と C がそのメソッドを異なった形でオーバーライドしていたら、D は B と C のどちらのメソッドを継承するのか、という問題である。
例えば、クラス Button は クラス Rectangle(見た目のため)と Mouse(マウスイベントのため)を継承し、Rectangle も Mouse も Object クラスを継承しているとする。ここで Button オブジェクトが equals メソッドを呼び出し、Button クラス自体にはそのメソッドは定義されていないとする。Rectangle と Mouse にはオーバーライドされた equals メソッドがそれぞれ定義されているとしたら、どちらを呼び出すべきか?
これが「菱形; diamond」問題と呼ばれるのは、クラス継承図の形状が菱形になるためである。クラス A が頂上にあり、B と C がそれぞれそこから枝分かれし、D がその2つの枝を再び1つにすることで、全体として菱形を形成する。
目次 |
対処法 [編集]
プログラミング言語毎にこの問題への対処法は異なる。
- C++ では、デフォルトでは個々の継承経路を独立して扱う。従って
Dオブジェクトには実際には2つの独立したAオブジェクトが内包され、Aのメンバの使用は適切に行われる。AからBへの継承とAからCへの継承が共に "virtual"(例えば "class B : virtual public A")である場合、C++ はこれを特別に扱い、1つのAオブジェクトだけを生成し、Aのメンバは正しく動作する。仮想継承と仮想でない継承が混在した場合、唯一の仮想のAと個々の仮想でない継承経路ごとのAが存在することになる。 - Common Lisp では、合理的なデフォルトの動作とそれをオーバーライドする能力を提供する。デフォルトでは、引数のクラス指定が最も具体的なメソッドが選択され、サブクラスの定義内でスーパークラスが指定された順番に従う。しかし、プログラマはこれをオーバーライドでき、メソッドごとの解決順序を指定したり、メソッド結合規則を指定したりできる。
- Eiffel では、ディレクティブを改名して選択することでこの問題を回避する。すなわち、上位クラスのメソッドを下位オブジェクトが使うときは明示的に指定する。これによって基底クラスのメソッド群がサブクラス間で共有でき、個々のクラスが基底クラスの個別のコピーを持っているように見なせる。
- Perl や Io では、継承するクラス群を順序リストで指定することで対処する。上述の例で言えば、クラス
Bの上位の方がクラスCの上位の前にチェックされるので、AのメソッドはBを通してのみ継承される。 - 2.1 以前の Python では、多重継承に対し、深さ優先-左から右の順でクラスのリストを生成する。Python 2.2 で導入され Python 3 では統一された、新スタイルクラス[1]では、全てのクラスは共通の基底クラス
objectから派生させるため、菱形継承への対処が重要になった。この時に同時に導入された順序は 2.2 でのみの採用にとどまったためここでは説明しない[2]。Python 2.3 以降および Python 3 では C3(w:C3 linearization)が採用された[3]。
その他の例 [編集]
クラスの多重継承ができない言語のうち、(Objective-C、PHP、C#、Javaなど)実装を持たないインタフェースのみを多重継承可能にしている言語がある(Objective-C ではプロトコルと呼ぶ)。実装を持たないため、インタフェースを多重継承しても、特定のメソッドやメンバ変数には常に1つの実装しかないので、あいまいさは発生しない。
菱形問題は継承に限ったことではない。A、B、C、D というヘッダファイルが互いに菱形を形成するように "#include" されている場合、同様の問題が発生しうる。プリプロセッサで処理された結果、A にあった宣言が B と C で異なった形に変えられ、"#ifdef" が適切に機能しないという状況がありうる。同様に、ミドルウェアスタックでも似たような問題が発生する。A がデータベース、B と C がそのキャッシュだとした場合、D が B と C にトランザクションのコミットを要求すると、A にはコミット要求が重複して届いてしまう。
注 [編集]
- ^ http://www.python.org/doc/newstyle/
- ^ 詳細は http://python-history.blogspot.jp/2010/06/method-resolution-order.html を参照のこと
- ^ http://www.python.org/download/releases/2.3/mro/