2012-07-13 43 views
1

我想要明確我對前述原則的理解,並通過閱讀wikipedia條目來完成此操作。瞭解Liskov替代原理

除了仍然給我悲傷的協變和逆變的概念外,維基百科還提到超類型的不變式必須保留在子類型和歷史約束或歷史規則中。基於這些最後兩個概念,我想出了這個小例子:

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(); 
    } 
} 

所以我的問題是:基於上述兩個概念,我是不是違反原則與這個例子嗎?如果不是,爲什麼?

非常感謝您提前。

回答

3

要違反LSP,你需要一個客戶端類,它在類接口上做了一些假設。這個假設不能以正式的方式來表達,有時它只是來自於用法的背景。

因爲你有一個枚舉類允許你添加元素。客戶端的假設可以是例如如果它添加了N個元素,那麼恰好N個元素可以從集合中讀取。然後你從你的集合中派生出一個集合,在添加時刪除重複的元素。客戶的期望現在是錯誤的,因爲即使添加了N個元素,有時也可以讀取少於N個元素。

對我來說,違反LSP需要一個定義一些期望的上下文。由於您的代碼沒有預期,因此不會違反LSP。

上下文的這種需要也意味着兩個類可以違背一個客戶端上下文的LSP,而相同的類可能不會違反其他上下文中的LSP。

+0

你的解釋提供了一個很好的觀點 – vibhu 2014-01-03 18:30:51

1

您在這裏似乎沒有違反LSP。我要離開一個小窗口,因爲理論上我們對FooBase的不變量一無所知,但是在看到沒有明顯問題時對這些結果進行合理猜測。

假設不變量是正確的,那麼派生類允許Name的值在基類不存在的對象的生命期內發生變化。如果不是一個小細節,這肯定會違反LSP:Name有一個protected二傳手。

受保護的二傳手應該意味着的FooBase筆者預計派生類對象的生命週期內改變Name的值,即使基類不會發生這樣做。將其與protected字段name進行對比,該字段不能具有用於獲取和設置其值的不同訪問級別。

+0

派生類可以設置的事實'Name'並不一定意味着一個期望,之後的實例已被公開曝光*修改可能會發生合法*。完全可能的是,setter只用於工廠方法,這些方法在工廠開始使用它之前可能不知道對象的正確名稱。 – supercat 2014-06-30 22:45:53

0

這個例子不夠健壯,不完全可以說,部分原因是因爲C#沒有清晰的方式來表示類不變量。或者,如果是這樣,我不知道它。

我想說你沒有違反一個不變量,因爲FooBase沒有保證Name不會改變或者表示Name的一個允許值的範圍。恰恰相反 - 通過在Name中加入一個受保護的setter,FooBase創建了一個期望值,可以通過派生類的內部機制來更改值

0

LSP違反本質上是一種違規行爲,如果超類型的任何
1.不變量由子類型或
2.超類型的先決條件違反了子類型或
3.後得到加強Sub類型削弱了條件。

上述三個條件必須事先確定,而界面設計,由正式的'設計合同'。
在您的示例中,FooBase沒有定義任何擴展類要遵守的任何正式規則。
此外,名稱設置者具有受保護的訪問權限。因此我不認爲LSP被侵犯。


有時也可以,如果違反LSP相對於測試案例分析是有幫助的。

這個測試案例是否有意義:
fooUser.Use(fooBase); //將打印「Serge」
String nameDerived = fooUser.Use(fooDerived); //將打印「新用戶」(假設這返回的名稱)

斷言(nameDerived等於「嗶嘰」)

如果這樣的測試案例存在(作爲由規格necessiated),那麼,很明顯上面的例子違反了LSP。

更何況在我的博客:http://design-principle-pattern.blogspot.in/2013/12/liskov-substitution-principle.html

相關問題