問題タブ [liskov-substitution-principle]
For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.
php - サブクラスは、Liskov 置換原則に従って public メソッドを持つことができますか?
次のクラス階層を検討してください。
にはメソッドがないため、クラスLaserPrinter
を親に置き換えることはできません。
それは、彼のヒエラルキーがリスコフの代用原理を破っているということですか? サブクラスが独自のパブリック メソッドを持つことは許可されていませんか?Printer
Printer
setFile
java - Liskov Substitution Principle のコンプライアンスを単体テストすることは良い習慣ですか?
次の名前のクラスがあるとしSprinter
ます。
そしてSprintGenius
継承する型Sprinter
:
論理的には、タイプごとに 1 つずつ、2 つの単体テスト クラスを作成する必要があります。
Sprinter
単体テスト内では、次のようになります。
SprintGenius
単体テスト内では、次のようになります。
上記の両方のテストで、10 秒以内に移動したメートルの量をテストします。
明らかに、これらの両方のテストは緑色になります。
しかし、リスコフの置換原理の違反についてはどうでしょうか?
実際、どのクライアント コードも、スプリンターが 9 秒以内に正確に 10 メートル走ることを期待する必要があります。
3 つの解決策 (最初の 2 つの解決策は、すべてのチームの開発者にルールが通知され、全員が Liskov の概念を十分に習得していなくても、認めて保持する必要があります)
1)Sprinter
クラスでは、各テストを複製しますが、今回Sprinter sprinter = new SuperGenius()
は 90 メートルを想定しています。=> 何が失敗するべきか、これこそまさに私たちが望んでいたことです! => Liskov の原則に違反しないようにします。
2)SprintGenius
クラスでは、まったく同じ期待に基づいて、基本クラスに基づいて各テストの同様の「クローン」を常に追加します。したがって、2 つの異なるテストがある場合、最終的に 4 つのテストになります。2 は Sprinter を として宣言しSprinter
、2 は として宣言Sprinter
しSprintGenius
ます。
3) 具象クラスから継承することはありません (この投稿を読んで最初に反応したのはこれだと思います:))。この問題が起こらないように。
多くの開発者が Liskov 原則を無視し、コンポジションや異なる継承階層などの別のより良い方法を使用する代わりに、具象クラスから継承するように誘惑されることが多いという事実に基づいて、Liskov 置換原則違反を防ぐためのベストプラクティスは何ですか?
開発者が私が書いたクラスを (私に言わずに..) 継承し、それを異種混合Sprinter
リストの共有された巨大なリスト内に挿入し、「こんにちは、奇妙な振る舞いだ!」そして何時間ものデバッグ時間...
もちろん、すべての具象クラスを「final」と宣言したくありません:)
c# - ReadOnlyCollection vs Liskov - 可変コレクションの不変表現を正しくモデル化する方法
リスコフ置換の原則では、サブタイプがスーパータイプの契約を満たす必要があります。私の理解では、これはReadOnlyCollection<T>
リスコフに違反することを伴います。 ICollection<T>
のコントラクトはエクスポーズAdd
とRemove
操作を行いますが、読み取り専用サブタイプはこのコントラクトを満たしません。例えば、
不変のコレクションが必要であることは明らかです。それらをモデル化する .NET の方法に何か問題がありますか? それを行うためのより良い方法は何ですか? IEnumerable<T>
少なくとも不変に見える一方で、コレクションを公開するという良い仕事をします。ただし、セマンティクスは大きく異なります。これは主にIEnumerable
、状態を明示的に公開しないためです。
私の特定のケースでは、 FSMをサポートするために不変のDAGクラスを構築しようとしています。最初に/メソッドが明らかに必要ですが、すでに実行されている状態マシンを変更できるようにしたくありません。DAG の不変表現と可変表現の類似性を表現するのに苦労しています。AddNode
AddEdge
現在、私の設計では、前もって DAG Builder を使用してから、不変グラフを一度作成する必要があります。その時点で、それは編集できなくなります。Builder と具体的な不変 DAG の間の唯一の共通インターフェイスはAccept(IVisitor visitor)
. おそらくより単純なオプションに直面して、これが過度に設計されている/抽象的すぎる可能性があることを懸念しています。同時に、NotSupportedException
クライアントが特定の実装を取得した場合にスローされる可能性のあるメソッドをグラフ インターフェイスに公開できることを受け入れるのに苦労しています。これを処理する正しい方法は何ですか?
c# - SOLID Essentials の不足点はありますか?
これについて非常に多くの記事を読みましたが、まだ 2 つの質問があります。
質問 #1 -依存関係の逆転について:
高レベルのクラスは低レベルのクラスに依存すべきではないと述べています。どちらも抽象化に依存する必要があります。抽象化は詳細に依存すべきではありません。詳細は抽象化に依存する必要があります。
例えば :
修正:それをctorに入れてください。
それがctorにある場合-クラスを使用するたびに送信する必要があります。
BirthdayCalculator
そのため、クラスを呼び出すときにそれを保持する必要があります。そんなことしていいの?私は、修正後も -
IList<Birthday> _birthdays
そこ (Birthday
インIList
) ではなく - であるべきだと主張することができますIList<IBirthday>
。私は正しいですか?
質問 2 - Liskov 置換について:
派生クラスは、それらの基本クラスに置き換え可能でなければなりません
またはより正確:
q(x) を、型 T のオブジェクト x について証明可能なプロパティとします。その場合、q(y) は、S が T のサブタイプである型 S のオブジェクト y に対して真でなければなりません。
(すでにこれを読んでいます)
例 :
私はクラスを持っています:
そして銀行は住宅ローン口座を開設したいと考えています - だから:
問題は、住宅ローンを受け取り、 Account
預金を行う機能がある場合に発生します。
ここでは、LSP に違反しています。
しかし、 私は理解していません。
オーバーライドされたすべてのメソッドは、オーバーライドされたときに異なるコードを実行するため、100%置き換え可能になることはありません!
定義では、「ロジックは基本クラスと同様に継続する必要があります(常に正の数を預ける(追加する))」については言及されていません。
例:
CheckingAccount
class とclassの両方MortgageAccount
が正の数をデポジットしていMortgageAccount
て、 db にもログを記録していたとしたら? それはまだ LSP を壊しますか? ブレーク/非ブレーキ LSP の境界は何ですか?
定義は、その境界が何であるかを定義する必要があります。そして、それについて何も言っていません。
私は何が欠けていますか?
java - Javaで代用性を失わずに委任デザインパターンを使用する方法はありますか?
この質問は、ここにある委任設計パターンに関するものです。
さまざまなエンティティを表すゲーム エンジン用のインターフェイスが多数あります。
- プレーヤー
- 車両
- メッシュ
- 等
これらはそれぞれレンダリングできるため、メソッドを含む Renderable インターフェイスを実装しますrender()
。
方法 1
委任を使用すると、例は次のようになります。
車をレンダリングしたいときはいつでも、 を呼び出すだけcar.renderable.render();
です。
この方法の問題は、リストを作成して反復処理できないことです。
方法 2
これを解決するために、Vehicle を Renderable に拡張することができます。
しかし、これの問題は、Car、Bicycle、Truck、Tank などを定義すると、これらのクラスのそれぞれに render() のコードを入力する必要があることです (これはおそらく同一です)。
Vehicle を実装するすべての具象クラスで render() を定義せずに、Vehicle インターフェイスで Renderable を拡張する利点を維持する方法はありますか?
c# - リスコフの置換原則(LSP)に準拠し、ポリモーフィズムの恩恵を受けるにはどうすればよいですか?
LSPは、「派生型は基本型の動作を変更してはならない」、つまり「派生型は基本型と完全に置き換え可能でなければならない」と述べています。
これは、基本クラスで仮想メソッドを定義すると、この原則に違反したことを意味します。
また、newキーワードを使用してdriveメソッドのメソッドを非表示にすると、この原則に違反します。
言い換えれば、ポリモーフィズムを使用すると、LSPに違反したことになります。
多くのアプリケーションで、基本クラスで仮想メソッドを使用しましたが、LSPに違反していることに気付きました。また、テンプレートメソッドパターンを使用する場合、私が頻繁に使用したこの原則に違反しています。
では、継承が必要で、ポリモーフィズムの恩恵も受けたい場合に、この原則に準拠するアプリケーションを設計するにはどうすればよいでしょうか。よくわかりません!
ここからの例を参照してください:http ://www.oodesign.com/liskov-s-substitution-principle.html
c# - Liskov Substitution Principleを破るクラス使用の落とし穴
私が最近取り組んだプロジェクトで、階層に属するクラスを受け入れるいくつかのメソッドに、次のようなコードがあることに気付きました。
さて、それはLSPのあからさまな違反だと思いました。なぜなら今、製品コード、ユニット テスト モック、または依存性注入インターセプターの一部としてdog のサブクラスを使用すると、このコードは同じようには機能しないからです。この特定のシナリオは、条件を次のように変更することで簡単に修正できると思います。
しかし、それは私に考えさせました:
クライアント コードで LSP を壊す可能性がある他の落とし穴はありますか?
アップデート
明確にするために、階層内のクラスを使用するコードで起こりうる落とし穴を探しています。私は認識しており、ひどく構築された階層に関する問題を探していません-(例: 長方形 - 正方形の問題)。コードが元のクラスを処理するのと同じ方法で動的モック、インターセプター、装飾されたクラス (LoggingDog など) をサポートすることを確認するために、何を探すべきかを見つけようとしています。
これまでのところ、回答とリンクを調べた後、クラスの型を直接使用することが唯一の落とし穴であることがわかります。つまり、GetType()
メソッドを直接使用するか、他のテクノロジーを介して使用します。ここにいくつかのコメントis
とas
演算子がありますが、この場合、サブクラスは元のクラスと同じ方法で評価されるため、基本型へのキャストでさえ LSP を壊しません。
oop - Address が PhoneNumber から継承する場合、どの OOP 原則に違反しますか?
クラスを持つと書かれている本があり、それから を継承PhoneNumber
するクラスを定義します。私はかつて、アドレスは電話番号ではないため、継承することはできないと言いました。それは「である」関係でなければなりません。たとえば、犬は動物であり、から継承させることができます。Address
PhoneNumber
Dog
Animal
しかし、LSP -- Liskov Substitution Principleに従わなければならないので、正方形は "is a" 長方形 (幅 == 高さ) であるため、"is a" ルールは実際には決定要因ではありませんが、LSP はできると言います。クラスを定義し、そのSquare
クラスから継承しRectangle
ます。aRect
英語での簡単な説明は、オブジェクトはメッセージsetWidthAndHeight(w, h)
に応答できますが、aSquare
正しく応答できず、プログラム全体が正しく実行できないということだと思います。
驚くべきことに、Address
クラスを継承するPhoneNumber
クラスは「is a」の関係に違反していますが、LSP には違反していません。正式には、どの OOP 原則に違反していますか?
software-design - LSP (Liskov Substitution Principles) 違反の回避
私は現在、Michael Feathers による「レガシー コードを効果的に使用する」を読んでいます。
LSP違反については理解していると思いますが、LSP違反を回避するのに役立つ経験則について何かを述べています。
- 可能な限り、具体的なメソッドをオーバーライドすることは避けてください。
- その場合は、オーバーライドするメソッドでオーバーライドしているメソッドを呼び出せるかどうかを確認してください。
数字の 2 がよくわかりません。これを明確にするのを手伝ってくれませんか?
c++ - なぜ関数ポインタは代入可能性を壊し、std::function は壊さないのですか?
通常のクラス階層があります。
そして、引数を 1 つ取る関数 - 基本クラス オブジェクトへの参照。
void (D&)
次に、型の関数ポインタを作成して格納したいと思いますb_set
。関数ポインター呼び出しに合法的に渡されるすべてのオブジェクトも B 型でなければならないため、これは有効な操作である必要がありますが、これは許可されていません。
そう...
- その無効な変換はどうですか?
- その無効な変換はなぜですか?
- これが許可された場合、何が壊れる可能性がありますか?
- そもそも std::function が問題をどのように回避するのでしょうか?