web-dev-qa-db-ja.com

このデコレータの実装は、リスコフの置換原則に違反していますか?

以下のDecoratorデザインパターンの実装を検討してください:

WordBankオブジェクトは文字列を格納し、getWords()メソッドを介してクライアントに返します。

デコレータクラスWordSorterは、WordBankのサブクラスです(デコレータパターンと同様)。ただし、これはgetWords()の実装であり、配列をクライアントに返す前に、一部の文字列を並べ替えて削除します。

たとえば、WordSorterは、バンク内の文字「a」で始まるすべての文字列を削除し、その後にのみ配列をクライアントに返します。

これは Liskov Substitution Principle に違反していますか? WordBankの実装によっては文字列を返すものもあれば、最初に文字列を並べ替えて一部のみを返すものもあるため、WordSorterを他のWordBankが使用されている場所で使用できると言っていいかどうかはわかりません。または私はこの原則を間違って理解していますか?

enter image description here

4
Aviv Cohn

WordSorteris-aWordBankなので、WordBankを使用すると、WordSorterで機能するコードも機能するはずです。 WorkBankの。一方、WordSorteris-not-aSomeWordBankです。コンパイラでは、WordSorterの代わりにSomeWordBankを使用することもできないため、問題は発生しません。

LSP違反がある可能性があります可能性がありますが、指定した最小仕様からのものではないようです。たとえば、WordSorterは、任意の文字列を追加して、後で同じ順序ですべて取得できることを保証しますか?次に、単語を並べ替えると実際にその契約が破られ、「正しい」WordBanksで機能するコードはWordSorterを置き換えることで破られます。そのような保証があるかどうかは、これまでに示した最小限のUML図からはわかりません。たとえば、WordBankの契約で、追加されたすべての単語がgetWordsの結果に含まれることが示されている場合、次のようになります。

bank.add(w);
AssertContains(bank.getWords(), w);

常に動作するはずです。 bankWordSorterである場合、そのコードは壊れます。そして、それはWordSorterが契約を破り、LSPに違反することの責任です。しかし、WordBankがそのような保証を提供しない場合、上記のコードは間違っています(同じようにasser x*y == 42は通常失敗します)、WordSorterは完全に正常に動作するサブクラスです。

11
user7043

与えられた一般的な説明では、具体的な型WordBankWordSorterWordFilterの方がいいかもしれません)は、両方が共通のインターフェースから実装する別個の型であるように聞こえますIWordSource、および/または共通の抽象基本クラスWordSourceBaseから継承します。具体的な型を他から継承するのではなく(メソッドが返すときに何を意味するかという問題は別として)アレイ)。 Wordホルダーに物を格納できるようにしたいコードが、アイテムが指定された順序で返されることを知りたい場合は、WordBankを要求する必要があります。単語のリストを提供できるものを必要とするが、そのリストがどのように生成されるかを気にしないコードは、IWordSourceを要求する必要があります。 IWordSourceを期待するコードに与えることができるが、提供されたデータを最初にフィルターするコードが必要なコードは、WordSorter/WordFilterを作成する必要があります。

インスタンス化可能なクラスは常にシールする必要があるとは言いませんが、インスタンス化可能なクラスからの継承を検討する多くの状況は、元のインスタンス化可能なクラスに抽象ベースから継承させることでより適切に処理できることを示唆します。提案された派生型も同じベースから継承します。元のインスタンス化可能なクラスの動作の詳細(派生型では異なる場合がある)に関係するコードは、その型の参照を要求できますが、抽象動作のみに関心があるコードは、その基本型の参照を受け入れることができます。 (これにより、元のタイプまたは派生物のいずれかを識別できます)。

1
supercat