2016-03-05 149 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显示警告。

相关问题