技術的には、継承で実現できるすべてのものは、委任でも実現できます。したがって、答えは「いいえ」になります。
継承を委任に変換する
次のクラスが継承で実装されているとしましょう。
public class A {
String a = "A";
void doSomething() { .... }
void getDisplayName() { return a }
void printName { System.out.println( this.getDisplayName() };
}
public class B extends A {
String b = "B";
void getDisplayName() { return a + " " + b; }
void doSomething() { super.doSomething() ; ... }
}
これらはうまく機能し、printName
Bのインスタンスを呼び出すと"A B"
コンソールに出力されます。
さて、委任でそれを書き直すと、次のようになります。
public class A {
String a = "A";
void doSomething() { .... }
void getDisplayName() { return a }
void printName { System.out.println( this.getName() };
}
public class B {
String b = "B";
A delegate = new A();
void getDisplayName() { return delegate.a + " " + b; }
void doSomething() { delegate.doSomething() ; ... }
void printName() { delegate.printName() ; ... }
}
Bで定義printName
し、Bがインスタンス化されたときにデリゲートを作成する必要があります。の呼び出しdoSomething
は、継承の場合と同じように機能します。ただし、への呼び出しはコンソールに出力printName
されます。"A"
実際、委任により、オブジェクトインスタンスにバインドされる「this」の強力な概念と、オーバーライドされたメソッドを呼び出すことができるベースメソッドが失われました。
これは、純粋な委任をサポートする言語で解決できます。純粋な委任では、委任内の「this」は引き続きBのインスタンスを参照します。これはthis.getName()
、クラスBからメソッドディスパッチを開始することを意味します。継承の場合と同じことを実現します。これは、委任があるSelfなどのプロトタイプベースの言語で使用されるメカニズムであり、組み込みの機能があります( Selfで継承がどのように機能するかをここで読むことができます)。
しかし、Javaには純粋な委任はありません。それではいつ立ち往生していますか?いいえ、実際には、もう少し努力すれば、自分たちでそれを行うことができます。
public class A implements AInterface {
String a = "A";
AInterface owner; // replace "this"
A ( AInterface o ) { owner = o }
void doSomething() { .... }
void getDisplayName() { return a }
void printName { System.out.println( owner.getName() };
}
public class B implements AInterface {
String b = "B";
A delegate = new A( this );
void getDisplayName() { return delegate.a + " " + b; }
void doSomething() { delegate.doSomething() ; ... }
void printName() { delegate.printName() ; ... }
}
基本的に、組み込みの継承が提供するものを再実装しています。それは意味がありますか?いいえ、違います。しかし、それは継承が常に委任に変換できることを示しています。
討論
継承は、基本クラスがサブクラスでオーバーライドされるメソッドを呼び出すことができるという事実によって特徴付けられます。これは、たとえば、テンプレートパターンの本質です。そのようなことは、委任では簡単に行うことはできません。一方、これが継承を使いにくくしているのです。ポリモーフィックディスパッチが発生する場所と、メソッドがオーバーライドされた場合の影響を理解するには、精神的な工夫が必要です。
継承とそれが設計にもたらす可能性のある脆弱性については、いくつかの既知の落とし穴があります。特にクラス階層が進化する場合。継承が使用されている場合、およびの平等に関していくつかの問題が発生する可能性もあります。しかし一方で、それはまだいくつかの問題を解決するための非常にエレガントな方法です。hashCode
equals
また、継承を委任に置き換えることができたとしても、それらは依然として異なる目的を達成し、互いに補完し合うと主張することができます。それらは、純粋な技術的同等性によって捉えられない同じ意図を伝えません。
(私の理論では、誰かがOOを始めたとき、それは言語の特徴のように認識されるため、継承を使いすぎてしまいがちです。次に、パターン/アプローチである委任を学び、それを好きになることも学びます。しばらくすると、私たちは両方のバランスを見つけ、どちらが良いかという直感の感覚を発達させます。ご覧のとおり、私はまだ両方が好きで、両方とも紹介される前に注意が必要です。)
いくつかの文献
継承と委任は、増分定義と共有の代替方法です。委任はより強力なモデルを提供すると一般に信じられてきました。このペーパーは、委任のすべてのプロパティをキャプチャする継承の「自然な」モデルがあることを示しています。独立して、継承をキャプチャする委任の能力に対する特定の制約が示されています。最後に、委任と継承の両方を完全にキャプチャする新しいフレームワークの概要を説明し、このハイブリッドモデルの影響のいくつかを検討します。
オブジェクト指向プログラミングにおける最も興味をそそると同時に最も問題のある概念の1つは、継承です。継承は、オブジェクト指向プログラミングを他の最新のプログラミングパラダイムと区別する機能と一般に見なされていますが、研究者がその意味と使用法に同意することはめったにありません。[...]
クラスの強力な結合と、継承によって引き起こされる不要なクラスメンバーの急増のために、代わりに構成と委任を使用するという提案が一般的になっています。文献での対応するリファクタリングの提示は、そのような変換が簡単な作業であると信じさせるかもしれません。[...]