Java継承の「拡張」を避ける理由


40

ジェイム・ゴスリングは言った

「可能な限り、実装の継承を避ける必要があります。」

代わりに、インターフェイスの継承を使用します。

しかし、なぜ?キーワード "extends"を使用してオブジェクトの構造を継承しないようにし、同時にコードをオブジェクト指向にする方法はありますか?

「書店で本を注文する」などのシナリオで、この概念を示すオブジェクト指向の例を教えてください。



問題は、1つのクラスしか拡張できないことです。

回答:


38

Goslingはextendsキーワードの使用を避けるとは言いませんでした...コードの再利用を実現する手段として継承を使用しないと言いました。

継承自体は悪くなく、オブジェクト指向構造の作成に使用する非常に強力な(必須の)ツールです。(何かのために使用されたときに正しく使用しないときしかし、他のオブジェクト構造を作成するよりも)それは非常に緊密に結合し、維持することは非常に困難であるコードを作成します。

ポリモーフィック構造の作成には継承を使用する必要があるため、インターフェイスでも同じように簡単に実行できます。したがって、オブジェクト構造を設計するときにクラスではなくインターフェイスを使用することを宣言します。あるオブジェクトから別のオブジェクトにコードをコピーアンドペーストすることを回避する手段として拡張機能を使用していることに気付いた場合は、おそらく間違って使用している可能性があり、この機能を処理し、代わりにコンポジションを介してそのコードを共有するための特別なクラスを使用する方が良いでしょう。

Liskov Substitution Principleを読んで理解してください。クラス(またはその点でインターフェース)を拡張しようとするときはいつでも、原則に違反しているかどうかを自問してください。そうであれば、不自然な方法で継承を使用する可能性が最も高くなります。それは簡単であり、多くの場合正しいことのように思えますが、コードの保守、テスト、および進化がより困難になります。

---編集 この回答は、より一般的な用語で質問に回答するためのかなり良い議論になります。


3
(実装)OO構造の作成には継承は必須ではありません。それなしでオブジェクト指向言語を持つことができます。
アンドレスF.

例を挙げていただけますか?継承なしでオブジェクト指向を見たことがありません。
ニュートピア

1
問題は、OOPの定義が受け入れられていないことです。(余談ですが、アラン・ケイでさえ、「メッセージ」ではなく「オブジェクト」に重点を置いて、彼の定義の一般的な解釈を後悔するようになりました)。これはあなたの質問への回答の試みです。私は、継承なしでOOPを見るのは珍しいことには同意します。したがって、私の議論は、それは本質的ではないというだけでした;)
Andres F.

9

オブジェクト指向プログラミングの初期の頃、実装の継承はコードの再利用の黄金のハンマーでした。あるクラスに必要な機能がある場合、やるべきことはそのクラスから公に継承することでした(これらはC ++がJavaより普及していた時代でした)。

ただし、実際には無料ではありませんでした。その結果、処理が困難なダイヤモンド型の継承ツリーを含む、理解がほとんど不可能な非常に複雑な継承階層が作成されました。これは、Javaの設計者がクラスの多重継承をサポートしないことを選択した理由の一部です。

ゴスリングのアドバイス(および他の声明)は、かなり深刻な病気に対する強力な薬であり、一部の赤ちゃんが風呂水で投げ出された可能性があると述べています。継承を使用して、クラス間の関係を真にモデル化することに何の問題もありませんis-a。ただし、別のクラスの機能を使用するだけの場合は、最初に他のオプションを調べた方が良いでしょう。


6

相続は最近非常に恐ろしい評判を集めています。回避する理由の1つは、「継承に対する構成」の設計原則に沿ったものです。これは基本的に、コードの記述を節約するために、それが本当の関係でない場合は継承を使用しないことを推奨します。この種の継承により、バグの発見と修正が困難になる場合があります。あなたの友人がおそらく代わりにインターフェースを使用することを提案していた理由は、インターフェースが分散APIに対してより柔軟で保守可能なソリューションを提供するためです。多くの場合、ユーザーのコードを壊すことなくAPIに変更を加えることができます。クラスを公開すると、ユーザーのコードが壊れることがよくあります。.Netでのこれの最良の例は、ListとIListの使用の違いです。


5

1つのクラスしか拡張できないのに対し、多くのインターフェイスを実装できるためです。

私はかつて、継承があなたが何であるかを定義すると聞いたが、インターフェイスはあなたが果たすことができる役割を定義する。

インターフェイスは、ポリモーフィズムのより良い使用も可能にします。

それが理由の一部かもしれません。


どうして下票?
マフムードホッサム

6
答えはカートを馬の前に置きます。Javaでは、原則としてGosling(Javaの設計者)が明示されているため、1つのクラスのみを拡張できます。それは法律であるため、子供が自転車に乗るときにヘルメットを着用する必要があると言っているようなものです。それは事実ですが、法律とアドバイスの両方が頭部外傷の回避から生じています。
JohnMcG

@JohnMcGゴスリングが行ったデザインの選択については説明していません。コーダーにアドバイスを与えるだけです。コーダーは通常、言語デザインに煩わされません。
マフムードホッサム

1

私もこれを教えられており、可能な場合はインターフェースを好みます(もちろん、意味のある場所では継承を使用します)。

私が考えていることの1つは、コードを特定の実装から切り離すことです。ConsoleWriterというクラスがあり、それをメソッドに渡して何かを書き出し、それをコンソールに書き込むとします。ここで、GUIウィンドウへの印刷に切り替えたいとします。さて、メソッドを変更するか、GUIWriterをパラメーターとして使用する新しいメソッドを作成する必要があります。IWriterインターフェースを定義することから始めて、メソッドにIWriterを取り込むようにした場合、ConsoleWriter(IWriterインターフェースを実装する)で開始し、その後GUIWriter(IWriterインターフェースも実装する)と呼ばれる新しいクラスを作成してから、渡されるクラスを切り替える必要があります。

もう1つ(C#には当てはまりますが、Javaについてはわかりません)、1つのクラスのみを拡張できますが、多くのインターフェイスを実装できます。Teacher、MathTeacher、HistoryTeacherという名前のクラスがあったとしましょう。MathTeacherとHistoryTeacherはTeacherから拡張されましたが、MathTeacherとHistoryTeacherの両方である人を表すクラスが必要な場合はどうでしょうか。一度に1つしか実行できない場合に、複数のクラスから継承しようとすると、かなり面倒になります(方法はありますが、最適ではありません)。インターフェイスを使用すると、IMathTeacherおよびIHistoryTeacherと呼ばれる2つのインターフェイスを使用し、その後、教師から拡張されてこれら2つのインターフェイスを実装する1つのクラスを作成できます。

インターフェースを使用することの1つの欠点は、時々、人々がコードを複製するのを見るということです(インターフェースを実装する各クラスの実装を作成する必要があるため)それに相当するJavaは何ですか)。

継承よりもインターフェイスを使用する最大の理由は、実装コードの分離であると考えていますが、継承は依然として非常に有用であるため、悪とは考えないでください。


2
「1つのクラスのみを拡張できますが、多くのインターフェイスを実装できます」これは、JavaおよびC#にも当てはまります。
FrustratedWithFormsDesigner

コード重複の問題の1つの可能な解決策は、インターフェイスを実装する抽象クラスを作成し、それを拡張することです。ただし、注意して使用してください。それが理にかなっていると私が見た少数のケースがあります。
マイケルK

0

クラスから継承する場合、そのクラスに依存するだけでなく、そのクラスのクライアントによって作成された暗黙のコントラクトを尊重する必要があります。ボブおじさんは、四角形のジレンマを拡張する基本的な正方形を使用して、リスコフの代替原則に微妙に違反するのがいかに簡単かという素晴らしい例を示しています。インターフェイスには、実装がないため、非表示のコントラクトもありません。


2
1つの注意点:オブジェクトが可変である場合、純粋なインターフェイスのみを使用しても、円楕円問題は引き続き発生します。
rwong
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.