2016-03-05 57 views
4

我剛剛安裝了微軟代碼合同。它是.NET Framework和Visual Studio插件的一部分。它提供運行時檢查和定義合同的靜態檢查。是否有可能在構造函數中違反Liskov替換原則?

該工具有四個警告級別,所以我設置了最高。

我已經宣佈設計違反Liskov替代原則的類。

public class Person 
{ 
    protected int Age { get; set; } 

    public Person(int age) 
    { 
     Contract.Requires(age > 0); 
     Contract.Requires(age < 130); 
     this.Age = age; 
    } 
} 

public class Child : Person 
{ 
    public Child(int age) : base(age) 
    { 
     Contract.Requires(age > 0); 
     Contract.Requires(age < Consts.AgeOfMajority); 
     Contract.Requires(age < 130); 
     this.Age = age; 
    } 
} 

public static class Consts 
{ 
    public readonly static int AgeOfMajority = 18; 
} 

LSP規定:

如果S是T的子類型,則類型T的對象可以與S型的 對象替換,而不改變任何編程

的 所需性質的

在我的例子中,違規行爲是:Person person = new Child(23);。我們應該可以做到這一點,但是我們不能這樣做,因爲孩子的年齡不會比年齡小於人類年齡所要求的年齡。

然而,分析結果令人驚訝CodeContracts: Checked 11 assertions: 11 correct。我的例子是錯誤的還是代碼合同沒有檢測到這樣的事情?

+2

我不認爲這違反了LSP。你的課沒有行爲,他們有相同的api。唯一的區別是他們的建築規則不同,但我從客戶使用這個班級的角度來解釋LSP的方式。任何處理「人物」的人都不應該知道關於兒童最大年齡的規則。在這種情況下,他們不需要。 – kai

+0

請注意,制定者不受限制。解決這個問題,你應該得到一個錯誤。 – Kevin

+0

@Kevin我已經實現了全年齡屬性並添加了需要的語句。仍然沒有警告。 – Landeeyo

回答

15

儘管LSP指定一個子類型不能在方法上放置更多限制性前置條件,但這不適用於構造函數,因爲您不使用構造函數以多態方式。

合同違規將爲new Child(23);,在分配給Person之前發生。

所以違規的例子是錯誤的,它不就得到儘可能創造亞型S的一個實例,更不用說更換它T.

+0

你可以提到任何說LSP不適用於構造函數的文章嗎?如果Child構造函數可能不同,我不會感到驚訝,例如需要一些額外的變量,但限制繼承變量,對我而言,這是一種代碼異味。 – Landeeyo

+1

這只是合乎邏輯的。 LSP討論使用子類型的實例。如果您無法創建該子類型的實例,那麼您甚至不需要進入LSP域。 – weston

+0

能夠將類型替換爲更多派生類型而不關心它的所有要點是,您可以多形地使用它們。你認爲在使用'Person'的代碼中會出現什麼問題,因爲年齡不低於18歲?我認爲,如果有的話,這裏的設計氣味是這個類的接口存在於原始的困擾中。 – kai

2

我要說的是Liskov替換支配的構造實例的行爲一類。因此,一個適當構造的兒童實例可以替代沒有問題的人。

您對如何構建子項有限制。我沒有看到框架沒有把這個標記爲問題。

6

有LSP違反了與鴨子一個著名的例子:

enter image description here

但是它不喜歡,我們可以違背它在構造函數中。比方說,我們有鴨,WildDuck類:

public abstract class Duck 
{ 
    public abstract string Quack(); 
    public double Weight { get; set; } 

    public Duck(double weight) 
    { 
     Contract.Requires(weight > 0); 
     this.Weight = weight; 
    } 
} 

public class WildDuck : Duck 
{ 
    public WildDuck(double weight) 
     : base(weight) 
    { 
     Contract.Requires(weight > 0); 
     this.Weight = weight; 
    } 

    public override string Quack() 
    { 
     return "wild quack"; 
    } 
} 

現在介紹ElectricDuck:

public class ElectricDuck : Duck 
{ 
    public Battery Battery { get; set; } 

    public override string Quack() 
    { 
     return "electric quack"; 
    } 

    public ElectricDuck(double weight, Battery battery) 
     : base(weight) 
    { 
     Contract.Requires(weight > 0); 
     Contract.Requires(battery != null); 
     this.Weight = weight; 
     this.Battery = battery; 
    } 
} 

public class Battery 
{ 
    public bool IsEmpty { get; set; } 
} 

在冷杉一眼看上去,似乎是它違反了LSP,因爲ElectricDuck需要比WildDuck或抽象的鴨子更多。但只要ElectricDuck提供Quack方法而沒有其他要求,情況就不是這樣。

如果ElectricDuck需要電池煥發 - 這是完全正確的,從一個LSP的角度來看:當我們添加的要求,以繼承的方法

public void Glow() 
{ 
    Contract.Requires(!this.Battery.IsEmpty); 
} 

LSP被侵犯:

public override string Quack() 
{ 
    Contract.Requires(!this.Battery.IsEmpty); 
    return "electric quack"; 
} 

而這種修改將導致CodeContracts顯示警告。

相關問題