4

ユース ケース: データ テンプレートを使用して、View を ViewModel に一致させています。データ テンプレートは、提供された具象型の最も派生した型を検査することで機能し、それが提供するインターフェイスを調べないため、インターフェイスなしでこれを行う必要があります。

ここでは例を簡略化し、NotifyPropertyChanged などを除外していますが、現実の世界では、View は Text プロパティにバインドされます。簡単にするために、TextBlock を持つ View は ReadOnlyText にバインドし、TextBox を持つ View は WritableText にバインドするとします。

class ReadOnlyText
{
    private string text = string.Empty;

    public string Text
    {
        get { return text; }
        set
        {
            OnTextSet(value);
        }
    }

    protected virtual void OnTextSet(string value)
    {
        throw new InvalidOperationException("Text is readonly.");
    }

    protected void SetText(string value)
    {
        text = value;
        // in reality we'd NotifyPropertyChanged in here
    }
}

class WritableText : ReadOnlyText
{
    protected override void OnTextSet(string value)
    {
        // call out to business logic here, validation, etc.
        SetText(value);
    }
}

OnTextSet をオーバーライドしてその動作を変更すると、LSPに違反しますか? もしそうなら、それを行うためのより良い方法は何ですか?

4

4 に答える 4

9

LSP は、サブクラスがそのスーパークラスの代用可能であるべきであると述べています (ここでスタックオーバーフローの質問を参照してください)。「書き込み可能なテキストは読み取り専用テキストの一種ですか?」という質問を自問してください。答えは明らかに「いいえ」です。実際、これらは相互に排他的です。そうです、このコードは LSP に違反しています。ただし、書き込み可能なテキストは読み取り可能なテキストの一種ですか (読み取り専用のテキストではありません)? 答えは「はい」です。したがって、答えは、それぞれの場合に何をしようとしているのかを調べ、次のように抽象化を少し変更することだと思います。

class ReadableText
{
    private string text = string.Empty;
    public ReadableText(string value)
    {
        text = value;
    }

    public string Text
    {
        get { return text; }
    }
}          

class WriteableText : ReadableText
{
    public WriteableText(string value):base(value)
    {

    }

    public new string Text
    {
        set
        {
            OnTextSet(value);
        }
        get
        {
            return base.Text;
        }
    }
    public void SetText(string value)
    {
        Text = value;
        // in reality we'd NotifyPropertyChanged in here       
    }
    public void OnTextSet(string value)
    {
        // call out to business logic here, validation, etc.       
        SetText(value);
    }
}     

明確にするために、Writeable クラスの Text プロパティで new キーワードを使用して、Readable クラスから Text プロパティを隠しています。http://msdn.microsoft.com/en-us/library/ms173152(VS.80).aspx
から: new キーワードを使用すると、置き換えられた基本クラス メンバーの代わりに新しいクラス メンバーが呼び出されます。これらの基本クラス メンバーは、隠しメンバーと呼ばれます。派生クラスのインスタンスが基本クラスのインスタンスにキャストされている場合でも、非表示のクラス メンバーを呼び出すことができます。

于 2010-10-22T13:03:31.453 に答える
8

ReadOnlyText.OnTextSet()スローする約束の仕様の場合のみ。

このようなコードを想像してください

public void F(ReadOnlyText t, string value)
{
    t.OnTextSet(value);
}

これがスローされなかった場合、それはあなたにとって意味がありますか? そうでない場合、WritableText は代用できません。

WritableTextText から継承する必要があるように私には見えます。ReadOnlyTextとの間に共有コードがある場合はWritableText、それを Text または両方が継承する ( から継承するText)別のクラスに配置します。

于 2010-10-22T12:47:38.167 に答える
2

契約にもよると思います。

ReadOnlyText のコントラクトに「Text を設定しようとすると例外がスローされる」と記載されている場合、LSP に違反していることは間違いありません。

そうでない場合でも、コードには扱いにくい部分があります。つまり、読み取り専用テキストのセッターです。

これは、特定の状況下で許容される「非正規化」です。多くのコードに依存しないより良い方法をまだ見つけていません。ほとんどの場合、Clean インターフェイスは次のようになります。

IThingieReader
{
    string Text { get; }
    string Subtext { get; }
    // ...
}

IThingieWriter
{
    string Text { get; set; }
    string Subtext { get; set; }
    // ...
}

...そして、適切な場合にのみインターフェースを実装します。Textただし、たとえば、書き込み可能であり、書き込み可能でないインスタンスに対処する必要がある場合、それSubtextはうまくいかず、多くのオブジェクト/プロパティに対して行うのは面倒です。

于 2010-10-22T12:48:30.553 に答える
0

はい、そうです。保護されたオーバーライド void OnTextSet(string value) も、「InvalidOperationException」タイプまたはそれから継承された例外をスローした場合はそうではありません。

基本クラス Text と、それを継承する ReadOnlyText と WritableText の両方が必要です。

于 2010-10-22T12:57:46.583 に答える