Liskov substitution principle, preconditions and a

2019-06-26 09:34发布

问题:

Liskov substitution principle (LSP) says:

Preconditions cannot be strengthened in a subtype.

In C#, I could violate the whole principle as follows:

public class A 
{
      public virtual void DoStuff(string text)
      {
            Contract.Requires(!string.IsNullOrEmpty(text));
      }
}

public class B : A
{
      public override void DoStuff(string text)
      {
            Contract.Requires(!string.IsNullOrEmpty(text) && text.Length > 10);
      }
}

But, what would happen if A.DoStuff would be an abstract method:

public class A 
{
      public abstract void DoStuff(string text);
}

public class B : A
{
      public override void DoStuff(string text)
      {
            Contract.Requires(!string.IsNullOrEmpty(text));
      }
}

Now A.DoStuff is contractless. Or its contract is just everything allowed.

So, is B.DoStuff precondition violating Liskov substitution principle?

回答1:

It depends on what defines the contract.

The LSP is a theoretical construct, it does not depend on a specific language or implementation, such as C#'s "Code Contracts" feature.

The contract can be defined by:

  • the method name
  • the method parameter names
  • the method comment
  • the return type and the method parameter types
  • "explicit" contracts such as Contract.Requires

On the last two will be verified by the compiler. However, the first three can be part of the contract as well! Consider the following example:

public interface StuffContainer
{
    void Add(string text);

    // Removes a string that has previously been added.
    void Remove(string text);
}

The name and the documentation of the Remove method define a clear precondition. Verifying in an implementation that the string to be removed has previously been added does not violate the LSP. Verifying that the string has at least 5 characters would violate the LSP.



回答2:

Yes, you can break the principle very easily, not only in C#.

It only states:

Subtype Requirement: Let phi(x) be a property provable about objects x of type T. Then phi(y) should be true for objects y of type S where S is a subtype of T.

In your example the type B does not fulfill the property of offering a method DoStuff that works with short texts, despite its supertype A fulfilling it. So the principle is violated.

It's up to the programmer to uphold the principle. A property could also be "it does the right thing", which you could easily break by having a subtype with a wrong implementation of a method.



回答3:

I'd argue that it's not. An abstract method by definition has no preconditions because there is no implementation. It would be the same as arguing if implementing an interface breaks LSP.

Saying A.DoSomething() is contracless is an untrue premise. A.DoSomehing() is undefined, therefore it can not have a contract further than its signature.