以下のDecoratorデザインパターンの実装を検討してください:
WordBank
オブジェクトは文字列を格納し、getWords()
メソッドを介してクライアントに返します。
デコレータクラスWordSorter
は、WordBank
のサブクラスです(デコレータパターンと同様)。ただし、これはgetWords()
の実装であり、配列をクライアントに返す前に、一部の文字列を並べ替えて削除します。
たとえば、WordSorter
は、バンク内の文字「a」で始まるすべての文字列を削除し、その後にのみ配列をクライアントに返します。
これは Liskov Substitution Principle に違反していますか? WordBank
の実装によっては文字列を返すものもあれば、最初に文字列を並べ替えて一部のみを返すものもあるため、WordSorter
を他のWordBank
が使用されている場所で使用できると言っていいかどうかはわかりません。または私はこの原則を間違って理解していますか?
WordSorter
is-aWordBank
なので、WordBank
を使用すると、WordSorter
で機能するコードも機能するはずです。 WorkBank
の。一方、WordSorter
is-not-aSomeWordBank
です。コンパイラでは、WordSorter
の代わりにSomeWordBank
を使用することもできないため、問題は発生しません。
LSP違反がある可能性があります可能性がありますが、指定した最小仕様からのものではないようです。たとえば、WordSorter
は、任意の文字列を追加して、後で同じ順序ですべて取得できることを保証しますか?次に、単語を並べ替えると実際にその契約が破られ、「正しい」WordBank
sで機能するコードはWordSorter
を置き換えることで破られます。そのような保証があるかどうかは、これまでに示した最小限のUML図からはわかりません。たとえば、WordBank
の契約で、追加されたすべての単語がgetWords
の結果に含まれることが示されている場合、次のようになります。
bank.add(w);
AssertContains(bank.getWords(), w);
常に動作するはずです。 bank
がWordSorter
である場合、そのコードは壊れます。そして、それはWordSorter
が契約を破り、LSPに違反することの責任です。しかし、WordBank
がそのような保証を提供しない場合、上記のコードは間違っています(同じようにasser x*y == 42
は通常失敗します)、WordSorter
は完全に正常に動作するサブクラスです。
与えられた一般的な説明では、具体的な型WordBank
とWordSorter
(WordFilter
の方がいいかもしれません)は、両方が共通のインターフェースから実装する別個の型であるように聞こえますIWordSource
、および/または共通の抽象基本クラスWordSourceBase
から継承します。具体的な型を他から継承するのではなく(メソッドが返すときに何を意味するかという問題は別として)アレイ)。 Wordホルダーに物を格納できるようにしたいコードが、アイテムが指定された順序で返されることを知りたい場合は、WordBank
を要求する必要があります。単語のリストを提供できるものを必要とするが、そのリストがどのように生成されるかを気にしないコードは、IWordSource
を要求する必要があります。 IWordSource
を期待するコードに与えることができるが、提供されたデータを最初にフィルターするコードが必要なコードは、WordSorter
/WordFilter
を作成する必要があります。
インスタンス化可能なクラスは常にシールする必要があるとは言いませんが、インスタンス化可能なクラスからの継承を検討する多くの状況は、元のインスタンス化可能なクラスに抽象ベースから継承させることでより適切に処理できることを示唆します。提案された派生型も同じベースから継承します。元のインスタンス化可能なクラスの動作の詳細(派生型では異なる場合がある)に関係するコードは、その型の参照を要求できますが、抽象動作のみに関心があるコードは、その基本型の参照を受け入れることができます。 (これにより、元のタイプまたは派生物のいずれかを識別できます)。