69

リスコフの置換原則についての私の理解は、真である基本クラスのいくつかのプロパティ、または基本クラスの実装された動作は、派生クラスでも真である必要があるということです。

これは、メソッドが基本クラスで定義されている場合、派生クラスでオーバーライドしてはならないことを意味すると思います。派生クラスの代わりに基本クラスを置き換えると、異なる結果が得られるためです。これはまた、(純粋でない)仮想メソッドを持つことは悪いことだと思いますか?

原理の理解が間違っているのではないかと思います。そうしないと、なぜこの原則が良い習慣であるのか理解できません。誰かが私にこれを説明できますか?ありがとう

4

6 に答える 6

67

基本クラスのメソッドをオーバーライドするサブクラスは、Liskov Substituion Principle によって完全に許可されています。

これは単純化しすぎているかもしれませんが、「サブクラスはそれ以上のものを必要とせず、それ以下のものを約束するべきではない」と覚えています。

ABCクライアントがメソッドでスーパークラスを使用している場合、クライアントは のサブクラスを問題なくsomething(int i)置き換えることができるはずです。ABCこれを変数の型の観点から考えるのではなく、おそらく事前条件と事後条件の観点から考えてみてください。

上記something()の基本クラスのメソッドに任意のABC整数を許可する緩和された前提条件がある場合、 のすべてのサブクラスも任意の整数を許可する必要があります。サブクラスは、パラメータがの整数であることを必要とする追加の前提条件をメソッドに追加することはできません。これは、Liskov Substitution Principle (つまり、より多くを要求する) に違反します。したがって、クライアントがサブクラスを使用していて、負の整数をクライアントに渡しても、に切り替える必要がある場合は壊れません。ABC GreenABCsomething()BlueABCsomething()GreenABC

逆に、基本ABCクラスのsomething()メソッドに事後条件 (ゼロの値を決して返さないことを保証するなど) がある場合、すべてのサブクラスも同じ事後条件に従う必要があります。

これが役立つことを願っています。

于 2009-11-14T19:35:35.000 に答える
14

アヒルのように泳ぎ、いんちきはアヒルが好きだが電池が必要な場合、リスコフの置換原理に違反するというよくある例があります。

簡単に言えば、誰かが使用しているベース Duck クラスがあります。次に、Duck と同じオーバーライドされた動作 (水泳、鳴き声など) を持つ PlasticDuck の紹介によって階層を追加しますが、これらの動作をシミュレートするにはバッテリーが必要です。これは基本的に、サブクラスの動作に追加の前提条件を導入して、以前はバッテリーなしで Base Duck クラスによって実行されていたのと同じ動作をバッテリーに要求することを意味します。これにより、Duck クラスのコンシューマーが不意を突かれる可能性があり、Base Duck クラスの期待される動作に基づいて構築された機能が壊れる可能性があります。

ここに良いリンクがあります - http://lassala.net/2010/11/04/a-good-example-of-liskov-substitution-principle/

于 2013-10-16T04:42:55.567 に答える
7

いいえ、派生クラスをそのベースと同じ方法で使用できる必要があることを示しています。これを壊さずにメソッドをオーバーライドする方法はたくさんあります。簡単な例では、C# の GetHashCode() はすべてのクラスの基本であり、それでもそれらのすべてを「オブジェクト」として使用してハッシュ コードを計算できます。私が覚えている限りでは、ルールを破る典型的な例は、Rectangle から Square を派生させることです。これは、Square は幅と高さの両方を持つことができないためです。ただし、.GetSize() を使用してベース Shape を保持することはできます。これは、すべての形状がこれを行うことができるためです。したがって、派生した形状を置換して Shape として使用することができます。

于 2009-11-14T18:38:47.640 に答える
6

基本メソッドで定義された動作を変更すると、オーバーライドすると Liskov Substitution Principle が破られます。つまり、次のことを意味します。

  1. 子メソッドの最も弱い前提条件は、基本メソッドの前提条件より強くしてはなりません。
  2. 子メソッドの事後条件は、親メソッドの事後条件を意味します。事後条件は、a) メソッドの実行によって引き起こされるすべての副作用、および b) 返された式の型と値によって形成ます

これら 2 つの要件から、スーパー メソッドから期待されるものに影響を与えない子メソッドの新しい機能は、原則に違反しないことを暗示できます。これらの条件により、スーパークラス インスタンスが必要な場合にサブクラス インスタンスを使用できます。

これらの規則に従わない場合、クラスは LSP に違反します。古典的な例は次のような階層です: class Point(x,y)ColoredPoint(x,y,color)拡張され、メソッドをPoint(x,y)オーバーライドし、色による等価性を反映するクラス。のインスタンスがあれば、同じ座標を持つ 2 つの点がこのセットで等しいと仮定できます。これはオーバーライドされたメソッドには当てはまりません。一般に、インスタンス化可能なクラスを拡張し、LSP を壊さずにメソッドで使用されるアスペクトを追加する方法はありません。equals(obj)ColoredPointSet<Point>equalsequals

したがって、この原則を破るたびに、潜在的なバグが暗黙のうちに導入され、コードによって期待される親クラスの不変条件が満たされない場合に明らかになります。ただし、現実の世界では、LSP に違反しない明確な設計ソリューションがないことが多いため、たとえば、@ViolatesLSPクラス アノテーションを使用して、クラス インスタンスをポリモーフィック セットまたはその他の種類で使用するのは安全ではないことをクライアントに警告できます。 Liskov 置換原理に依存するケースの。

于 2011-04-27T21:24:48.253 に答える
2

原則を説明する方法は文字通り正しいと思います。純粋な仮想メソッドまたは抽象メソッドをオーバーライドするだけで、それに違反しないことが保証されます。

ただし、クライアントの観点から原則を見ると、つまり、基本クラスへの参照を取るメソッドです。このメソッドが、渡されたインスタンスのクラスを特定できない (そして、特定しようとせず、特定する必要がない) 場合も、原則に違反していません。そのため、基本クラスのメソッドをオーバーライドすることは問題にならない場合があります (一部の種類のデコレーターは、プロセスで基本クラスのメソッドを呼び出してこれを行う場合があります)。

クライアントが渡されたインスタンスのクラスを見つける必要があると思われる場合、既存のルーチンを変更するのではなく、メンテナンス作業の一環として新しいクラスを追加する必要があるため、メンテナンスの悪夢に直面しています。( OCPも参照)

于 2009-11-14T22:55:07.630 に答える