LinkedListを拡張するスタック。Liskov Substitution Principleの違反?


13

クラスLinkedListは、add_first()、add_last()、add_after()、remove_first()、remove_last()、remove()などの関数とともに存在します

現在、push()、pop()、peek()、またはtop()などの機能を提供するクラスStackがあり、これらのメソッドを実装するためにLinkedListクラスメソッドを拡張しています。これは、リスコフ代替原理の違反ですか?

たとえば、リンクリストのn番目の位置にノードを追加する場合のadd_after()の場合を考えます。これは基本クラスで実行できますが、Stackクラスでは実行できません。ここで事後条件が弱くなっていますか、それともadd_after()メソッドを変更してスタックの一番上に追加しますか?

また、違反ではない場合、これは悪い設計ですか?そして、LinkedListクラスを使用してStack機能をどのように実装しますか?


6
質問クラスをそれ自体のリストのサブクラスとして定義することの欠点は何でしょうか?少し異なる問題について質問しますが、ほとんどの答えはここにも当てはまります。リストから継承するよりも、プライベートメンバーとしてリストを使用してスタックを作成する方がよいでしょう。
アモン

7
はい。これは、スタックの内部表現をリンクリスト以外(配列など)に変更できないため、設計が悪いです。また、スタックが通常サポートしない操作を公開しています。継承の代わりに構成を使用します。
リー

2
拡張しLinkedListますか?「継承よりも構成を優先する」と言ったときに、特定のJoshua Bloch(Javaコレクションフレームワークの設計で主要な役割を果たした)のアドバイスに注意する必要がLinkedListあります。
corsiKa

1
「スタックは一種のリンクリストである」というのは本当の声明ではないので、リスコフの置換原理を満足させることはできないと思います。「スタック」は実際にはインターフェースです(概念的にはJava構成ではありません)。スタックはリンクリストを使用して実装できます。他の人が言ったように、あなたはLinkedList舞台裏で重荷を持ち上げるタイプのプライベートデータメンバーを使用します(組成)。そうすれば、コードのユーザーが誤ってリストを必要とする場所でスタックを使用したり、その逆を行うことはできません。
ジャスミン

1
賢者になるものは逆になるようです。これを行っていた場合、リンクリストクラスに「スタック」インターフェイスを完全に実装できる可能性があります。それ以外の場合、スタックは概念であるため、これは間違った方法です。リンクリストは、とりわけスタックとして機能する実装です。
ヴァリティ

回答:


31

現在、push()、pop()、peek()、またはtop()などの機能を提供するクラスStackがあり、これらのメソッドを実装するためにLinkedListクラスメソッドを拡張しています。これは、リスコフ代替原理の違反ですか?

いいえ。サブタイプにメソッドを追加してもまったく問題ありません。

たとえば、リンクリストのn番目の位置にノードを追加する場合のadd_after()の場合を考えます。これは基本クラスで実行できますが、Stackクラスでは実行できません。

これ LSPの違反です。LSPは、サブタイプのインスタンスは、プログラムの望ましいプロパティを変更せずに、スーパータイプのインスタンスに代用可能でなければならないと述べています。サブタイプがメソッドを削除すると、そのメソッドを呼び出すコードがクラッシュします(またはNoMethodError例外、またはそれらの行に沿って何かが発生します)。明らかに、「クラッシュしない」ことが望ましいプロパティです。

ここで事後条件が弱くなっていますか、それともadd_after()メソッドを変更してスタックの一番上に追加しますか?

add_after()この方法でメソッドを変更すると、履歴ルール(ルールの最も重要なもの)に違反することになり、LSP違反の修正に役立ちません。

そして、LinkedListクラスを使用してStack機能をどのように実装しますか?

コンポジションを使用して。

注:上記書いたすべては、サブタイプとサブクラスを混同する言語にのみ適用されます!LSPはサブクラスではなくサブタイプ化に関するものです。2つを混同しない言語ではStack、サブクラスをLinkedListサブタイプにしない限り、のサブクラスを作成することは完全に受け入れられますLinkedList


9
履歴ルールとは何ですか?インターネットで見つけられませんでした。
ザパドロ

10
サブタイプのオブジェクトを操作し、スーパータイプのメソッドを介して観察する場合、スーパータイプのオブジェクトでは観察できなかった履歴を観察することは不可能でなければなりません。このルールは、非機能言語にLSPを適用可能にするものです。他のすべてのものは、それよりずっと前にすでに知られていました。事前条件と事後条件のルールは、関数タイプの共分散ルールと反分散ルールの再定式化です。履歴ルールにより、このすべてが可変データに適用されます。それがなければ、LSPは純粋に関数型の言語でのみ役立ちます。
ヨルグWミットタグ

2
ウィキペディアの記事の説明はかなり良いと思います:wikipedia.org/wiki/Liskov_substitution_principleしかし、いつものように、あなたは本当に元のソースを読むべきです。
ヨルグWミットタグ

@JörgWMittag:型は、「履歴」が観察されるかどうかについての契約保証に含めるのが合理的だと思いますが、すべてのスーパータイプがすべての観察シーケンスを生成できる必要はないと思いますこれは、サブタイプのオブジェクトで可能になる可能性があります。単に、スーパータイプは、スーパータイプのコンシューマーがそのようなシーケンスを観察する可能性を文書化するだけです。
supercat

1

すべてがJörgW Mittagによって既に対処されているため、次の部分について少し詳しく説明します。

ここで事後条件が弱くなっていますか、それともadd_after()メソッドを変更してスタックの一番上に追加しますか?

基本的に、ある階層がLSPに違反しているかどうか疑問がある場合、それはあなたがどの契約を課すかに依存します。それで、どんな契約がadd_afterありますか?「n番目の位置または最上部にノードを追加する」など、どんなに狂ったように聞こえても、問題ない場合は、事後条件が満たされ、LSPは違反されません。それ以外の場合は、LSP違反です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.