2012-04-17 14 views
1

私はこのコードを持っている:オーバーライドされたメソッド引数を絞り込むと、Liskov置換原則違反になりますか?

abstract class Entity 
{ 
// blah-blah-blah 
} 

abstract class BaseCollection 
{ 
    public void add(Entity entity); 
} 

をそして私は、エンティティとBaseCollectionクラスから派生:

class User extends Entity 
{ 
} 

class UserCollection extends BaseCollection 
{ 
    public void add(User user) { // blah-blah-blah } 
} 

これはリスコフの置換原則違反の例ですか?そうであれば、どのように問題を解決できますか? Userとして

答えて

0

Entityのサブタイプであり、(UserCollectionを介して)BaseCollectionこのようなオブジェクトを追加するために、完全に合理的である - 各ユーザは他方では動作しないであろう、BaseCollectionが期待されるUserCollectionを渡すEntity

あります:Entityを追加できるようになっていますが、Userが必要です。つまり、UserCollectionから要素を取得すると、Userを受け取った後にEntityが表示されることがあります。

+0

お返事ありがとうございます。 私の経験では、オーバーライドされたメソッドの引数の型を狭くする必要がある場合がよくあります。しかし、これはLSPに違反します。 どうすればこの問題に取り組むことができますか? –

+0

あなた自身がLSPに違反していることが判明した場合、実際にはis-a関係がないことを意味します。継承は正しい選択ではありません。構成、いくつかの種類のテンプレート/ジェネリックソリューション、あるいは単純に2つの非関連クラスを代わりに考えてみてください。 – Attila

+0

OKですが、実際の生活状況をモデル化する必要があることもあります。 たとえば、 'call(Doctor doctor)'メソッドを持つ 'Human'クラスがあります。そして、「子」クラスは「人間」を拡張します。しかし、「子供」は医者を受け入れることができません。彼は '医者 'を拡張する'小児科医 'だけを必要とする。だから、 '子ども'はこのようにメソッドをオーバーライドします: 'コール(小児科医)'。 'Child'は明らかに' Human'との関係があります。しかし、このクラスモデルはLSPに違反します。違反を克服する方法は? –

0

他の実装のEntityをUserCollectionに追加できないため、Liskov Substitution Principleの違反です。 BaseCollectionへの参照を持つユーザーは、ユーザー以外のエンティティを提供する場合、UserCollectionsが展開する実装を期待しません。

私は、明示的に絞り込みを言い、言語を指定しなかったので、UserCollection.addがBaseCollection.addを置き換えていると仮定しています。

リスコフ置換原理に従う場合、メソッドパラメータは反変であり、共変ではありません。 http://en.wikipedia.org/wiki/Liskov_substitution_principle

+0

"Userの他の実装を追加できませんでした" BaseのプロパティがDerivedで保持するLSP requres: 'Entity'に当てはまることは' User'に当てはまります。しかし、ユーザの別の実装のプロパティは、「ユーザ」を保持する必要はありません – Attila

+0

「ユーザ」はその文の「エンティティ」であったはずです。キャッチをありがとう。 –

0

BaseCollectionの契約は、そのadd方法は、合法的に、その後UserCollectionの継承add方法も同様に行う必要があり、Entityから導出された任意のオブジェクトを渡すことができ、そしてそうする失敗はLSPの違反になることを指定した場合。 UserCollectionのみオーバーロードする可能性が特に適切ではないであろうけれども、元add方法は、Entityに由来する任意のオブジェクトで使用することができれば、LSPに違反しないタイプUserのオブジェクトを受け入れadd(上書きしない)を含んで有します。

場合は、代わりのaddは、当該の方法は、派生クラスのベースでsetItem(int index, Entity value)setItem(int index, User value)のようなものだった、との契約は、それが唯一の外に読まれていたオブジェクトを操作することが保証されていたことを指定した場合同じコレクションの場合、UserCollectionを読み取ると、Userのインスタンス以外は決して返されません。メソッドは、LSPに違反することなくUserのインスタンスではなかったすべてのオブジェクトを正当に拒否できます。 setItemメソッドがuserのインスタンスではないすべてを拒否しようとしている場合は、userしか受け付けないオーバーロードを持つことが有用で適切かもしれません。たとえ継承されたsetItemメソッドがvalueUserのインスタンスを識別したことを確認する必要があったとしても、その型の引数を受け入れたオーバーロードは起こりません。このようなオーバーロードを追加する際の最大の注意点は、同じことを行う2つの非開封仮想メソッドの存在を避けるべきです。オーバーロードを追加する場合は、渡された引数をタイプUserに変換し、メソッドのオーバーロードされたバージョンにチェーンするように、ベースクラスのメソッドをオーバーライドしてシールする必要があります。

配列は、後者の形式の契約および継承をサブスクライブします。タイプAnimal[]の変数は、Cat[]への参照を保持することができます。 Cat[]を識別するAnimal[]に任意のAnimalを格納しようとすると失敗する可能性がありますが、Animal[]から読み取られたAnimalは同じ配列に「適合」することが保証されています。これにより、問題の配列の型を知らなくても、コードが任意の参照型の配列内の要素をソートまたは置換することが可能になります。

関連する問題