简体   繁体   中英

Is it possible to violate Liskov Substitution Principle in a constructor?

I've just installed Microsoft Code Contracts. It's a part of .NET Framework and Visual Studio add-on. It provides runtime checking and static checking of defined contracts.

The tool has four warning levels so I set up the highest.

I've declared classes designed to violate Liskov Substitution Principle.

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 states:

if S is a subtype of T, then objects of type T may be replaced with objects of type S without altering any of the desirable properties of that program

In my example the violation would be this asignment: Person person = new Child(23); . We should be able to do this, but we can't because children can't be older than some age smaller than required by person class.

The result of analysis however is surprising CodeContracts: Checked 11 assertions: 11 correct . Is my example wrong or Code Contracts don't detect such things?

While it's true that LSP specifies a subtype can't place more restrictive preconditions on methods, this doesn't apply to constructors as you do not use constructors in a polymorphic way.

The contract violation would be new Child(23); which occurs before assigning to a Person .

So the example violation is wrong, it doesn't get as far as creating an instance of subtype S, let alone replacing it for T.

There's a famous example of LSP violation with the duck:

在此输入图像描述

However it's not like we can violate it in a constructor. Let's say we have Duck and WildDuck classes:

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";
    }
}

Now let's introduce 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; }
}

At firs glance it may seem it violates LSP because ElectricDuck requires more than WildDuck or abstract Duck. But it's not true as long as the ElectricDuck provides Quack method without additional requirements.

If ElectricDuck requires Battery to glow - it's perfectly correct from an LSP standpoint:

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

LSP is violated when we add the requirement to inherited method:

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

And this modification will cause CodeContracts to show a warning.

I would say that liskov substitution governs the behavior of constructed instance of a class. So a properly constructed instance of a Child can be substituted for a Person with no problem.

You have constraints on how to construct a Child. I don't see the framework's not flagging this as a problem.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM