下の図は私の講義スライドの例です。私は C++ について知っています。
また、Ruby の知識がある私にとっては、Vector はArray
ランダム アクセスがあり、Stack はそうでないため、継承は合理的であると思われます。誰でももっと簡単な例を教えてもらえますか?または説明私の理解の何が問題なのですか?
下の図は私の講義スライドの例です。私は C++ について知っています。
また、Ruby の知識がある私にとっては、Vector はArray
ランダム アクセスがあり、Stack はそうでないため、継承は合理的であると思われます。誰でももっと簡単な例を教えてもらえますか?または説明私の理解の何が問題なのですか?
この場合、継承が良い考えではない理由の鍵は次のとおりです。
Vector is like Array with random access and Stack is the one without
大まかに言えば、継承は追加の動作を提供するためにあります。動作を削除する合理的な*方法はありません。
したがって、Stack
extendsの場合Vector
、クラスによって提供されるランダムアクセス動作 (およびコントラクト) が継承されVector
ますが、これは望ましくありません。
is-a** の観点から継承について考えてみましょう。Stack
a is-a と言うのは理にかなっていVector
ますか? そうでない場合は、拡張しないでくださいVector
。
Stack
aを使用して a を実装できるという事実は、インターフェイスVector
には関係ありません。Stack
@Patashu が指摘したように、必要に応じて、契約を変更せずに実装を変更できるようにする必要があります。たとえば、Vector
. 延長する場合、Vector
これは不可能です。コンポジションを使用する場合、それはかなり簡単です。
*もちろん、メソッドをオーバーライドして不要な動作を削除することはできますが、それはVector
契約を破り、ユーザーにとって非常に混乱を招くことになります。そのため、通常、これを行うことは合理的ではありません。
* *is-aには、ソフトウェア アーキテクチャの他のものと同様に、独自の落とし穴があります。どのクラスが何から継承するかを決定する際に特に注意する必要がある理由の良い例については、 Circle-Ellipse 問題を参照してください。
他の人のために書かれているコードベースの観点から考えると、他の人の使用をサポートする必要があるか、またはあなたのコードが他のコードベースを使用している場合に役立ちます。自分のコードだけの場合は、すべてを制御できるので簡単に変更できますが、他の人をサポートする必要がある場合は、どのような変更が他の人のコードを壊す可能性があるかを認識しておく必要があります。
1) クラスから継承する場合、クラスと同じコントラクトをサポートする必要があります。契約とは、一般向けのメソッドが、クラスが実行できる一連の約束を形成することを意味します。これは、a) そのコントラクトがクラスにとって扱いにくい場合、b) 継承元のクラスのコントラクトが変更された場合に問題になる可能性があります。
2) 継承の代わりに合成することで、クラスの実装に使用するものを公開変更を壊すことなく変更できるため、「スタック実装に Vector を使用したくない」と決めた場合のように、はるかに迅速かつ安価になります。 、代わりに DifferentVector を使用したい」スタックが Vector のサブクラスであると仮定すると、コードが壊れます。
3) さらに、ベクターでバックアップされるスタックに継承を使用していない場合でも、Stack と Vector の両方に ICollection インターフェイスを実装することができます (たとえば)。そもそも継承を行うことの主な利点です。