1

私は、ウィキペディアのエントリを何度も読んで、前述の原則の理解を深めようとしています。

いまだに私を悲しませる共分散と反分散の概念はさておき、ウィキペディアでは、スーパータイプの不変条件はサブタイプと履歴制約または履歴ルールで保持する必要があるとも述べています。これらの最後の 2 つの概念に基づいて、この小さな例を思い付きました。

class Program
{
    static void Main(string[] args)
    {
        var fooUser = new FooUser();

        var fooBase = new FooBase("Serge");

        var fooDerived = new FooDerived("Serge");

        fooUser.Use(fooBase); //will print "Serge"
        fooUser.Use(fooDerived); //will print "New User"

        Console.ReadKey();
    }
}

public class FooUser
{
    public void Use(IFoo foo)
    {
        foo.DoSomething();
        Console.WriteLine(foo.Name);
    }
}

public interface IFoo
{
    string Name { get; }
    void DoSomething();
}

public class FooBase : IFoo
{
    public string Name { get; protected set; }

    public FooBase(string name)
    {
        Name = name;
    }

    public virtual void DoSomething()
    {
    }
}

public class FooDerived : FooBase
{
    public FooDerived(string name) : base(name)
    {
    }

    public override void DoSomething()
    {
        Name = "New Name";

        base.DoSomething();
    }
}

私の質問は、上記の 2 つの概念に基づいて、この例の原則に違反していますか? そうでない場合、なぜですか?

事前にどうもありがとうございました。

4

4 に答える 4

3

LSP に違反するには、クラス インターフェイスで何らかの仮定を行うクライアント クラスが必要です。仮定は、正式な方法で正確に表現する必要はありません。場合によっては、使用のコンテキストから来ることもあります。

要素を追加できる列挙可能なクラスがあるとします。クライアントは、たとえば、N 個の要素を追加する場合、正確に N 個の要素をコレクションから読み取ることができると想定できます。次に、追加時に重複する要素を削除するコレクションからセットを派生させます。N 個の要素が追加されたとしても、LESS THAN N 個の要素を読み取ることができる場合があるため、クライアントの期待は現在間違っています。

私にとって、LSPの違反には、いくつかの期待を定義するコンテキストが必要です。あなたのコードには何も期待されていないので、LSP に違反していません。

このコンテキストの必要性は、2 つのクラスが 1 つのクライアント コンテキストの LSP に違反する可能性がある一方で、同じクラスが他のコンテキストでは LSP に違反しない可能性があることも意味します。

于 2012-07-13T15:46:16.783 に答える
1

ここでは LSP に違反していないようです。理論的には の不変量に​​ついては何もわかっていないため、疑問の余地を残していますがFooBase、これらについて合理的な推測を行うと、明らかな問題は見られません。

Name不変条件が良好であると仮定すると、基本クラスでは変更できないオブジェクトの存続期間中に、派生クラスでは値の変更が許可されるという履歴原則の問題が残ります。これは確かに LSP に違反しているように見えNameますprotected

プロテクト セッターとは、 の作成者が、派生クラスがオブジェクトの存続期間中にの値を変更することをFooBase 期待していることを意味する必要があります。これを、値を取得および設定するための異なるアクセス レベルを持つことができないfieldNameと対比してください。protectedname

于 2012-07-13T15:05:01.263 に答える
0

C# にはクラスの不変条件を明確に表現する方法がないため、この例は言うほど堅牢ではありません。というか、あったとしても私は気づいていません。

FooBase は Name が変更されないこと、または Name の許容値の範囲を表現しないことを保証していないため、不変条件に違反していないと思います。まったく逆です。Name に保護されたセッターを含めることにより、FooBase は派生クラスの内部メカニズムによってその値を変更できるという期待を作成しています。

于 2012-07-13T15:06:02.153 に答える
0


1. スーパータイプのインヴァリントがサブタイプによって違反されている場合、または
2. スーパータイプの前提条件がサブタイプによって強化されている場合、または
3. 事後条件がサブタイプによって弱められている場合、LSP違反は本質的に違反です。

上記の 3 つの条件は、正式な「契約による設計」によって、インターフェイスの設計中に事前に決定する必要があります。
あなたの例では、 FooBase は、拡張クラスのいずれかが従うべき正式な規則を定義していません。
さらに、ネームセッターは保護されたアクセスを持っています。したがって、LSP に違反しているとは思いません。

しかし

テスト ケースに関して LSP 違反を分析すると役立つ場合もあります。

このテスト ケースは理にかなっていますか:
fooUser.Use(fooBase); // "Serge"
文字列を出力します nameDerived = fooUser.Use(fooDerived); // "New User" を出力します (これが名前を返すと仮定します)

assert( nameDerived equals "Serge" )

そのようなテスト ケースが存在する場合 (仕様により必要とされる)、上記の例は明らかに LSP に違反しています。

私のブログでもっとそう:http://design-principle-pattern.blogspot.in/2013/12/liskov-substitution-principle.html

于 2013-12-16T13:16:57.813 に答える