4

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?

Peter O.
  • 32,158
  • 14
  • 82
  • 96
Matías Fidemraizer
  • 63,804
  • 18
  • 124
  • 206
  • `A.DoStuff` has no precondtion, while `B.DoStuff` does so you have strengthened it. – Lee Dec 04 '16 at 21:49
  • @Lee The discussion could be if an abstract method, being just *metadata*, couldn't be considered to not provide preconditions, because since it has no behavior, the fact that it doesn't provide a precondition shouldn't mean that *it has no preconditions*. I believe that it's a simple case but it might require a complex analysis to determine if it's really breaking LSP... – Matías Fidemraizer Dec 04 '16 at 22:09
  • @InBetween why you dropped your answer? – Matías Fidemraizer Dec 04 '16 at 22:13
  • I don't know what you mean by abstract methods providing metadata, but if a method does not explicitly state any preconditions then it has none. – Lee Dec 04 '16 at 22:22
  • 1
    @zerkms - abstract methods can have pre/post conditions the same as any other. – Lee Dec 04 '16 at 22:26
  • @Lee you should elaborate more on your last statement – Matías Fidemraizer Dec 04 '16 at 22:31
  • @MatíasFidemraizer see `ContractClass` and `ContractClassFor` attributes – zerkms Dec 04 '16 at 22:31
  • @Lee With contract classes, even interfaces can have pre, post-conditions, and invariants... – Matías Fidemraizer Dec 04 '16 at 22:31
  • Yep, and that's what they exactly meant - there should be no difference in how you treat a "normal" and abstract method or interface: if they don't have a precondition, then adding any* strengthengs it – zerkms Dec 04 '16 at 22:33
  • Because the more I thought it through the more convinced I was that yes, it is in fact a LSP violation and that my answer is perhaps wrong. I'm still mulling it over...but I'll un delete the answer, open another point of view – InBetween Dec 04 '16 at 22:40
  • I'm saying that pre/post conditions must be stated expiclity so by not providing any, you are implicitly stating there are none. `A.DoStuff` has no preconditions, while `B.DoStuff` does `B` is strengthining the precondition and violating LSP. `A.DoStuff` being abstract is irrelevant since abstract methods can have pre/post conditions like any other method. – Lee Dec 04 '16 at 22:42
  • @zerkms you dropped some comment where you defended the opposite arguing that implementations of interface methods might break LSP, and now you're stating that you would treat regular, abstract and interface methods without any distinction in terms of not violating LSP. I've opened this Q&A because I feel that it's not 100% clear, we can do mistakes in our arguments and it's about getting a convincent answer to my (or even our) concerns. – Matías Fidemraizer Dec 04 '16 at 22:51
  • @Lee So when you create a contract class of a given interface, you would also violate LSP. An abstract method and an interface method are almost the same monster: in C#, they're just metadata. – Matías Fidemraizer Dec 04 '16 at 22:53
  • I don't understand what you mean by 'metadata'. If you create a contract class and provide a contract for abstract methods or interfaces then all implementations must respect that by not strengthening preconditions or weakening postconditions. In your example you aren't providing any preconditions for `A.DoStuff` so there are none and therefore none can be provided by any subclasses. – Lee Dec 04 '16 at 22:57
  • @MatíasFidemraizer I dropped it because I forgot it is technically possible (I personally find pre/post-conditions for both abstract methods and interfaces awkward hence never used it). – zerkms Dec 04 '16 at 22:57
  • @zerkms In my case I've found contract classes to be extremely useful, you can define how to implement your interfaces or abstract classes, not only *what* to implement – Matías Fidemraizer Dec 04 '16 at 23:01
  • *Now `A.DoStuff` is contractless*. NO, no, no. `A` **is** the contract! In a general sense, every base class is a contract for its derived class. – Olivier Jacot-Descombes Nov 23 '17 at 16:21

3 Answers3

2

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.

Heinzi
  • 167,459
  • 57
  • 363
  • 519
  • Note that I wanted to explain the whole concern with C# and code contracts to just discuss about the topic based on an actual sample. – Matías Fidemraizer Dec 04 '16 at 22:18
  • Anyway, your veredict is that the whole case I've explained in my question wouldn't necessarily violate LSP? – Matías Fidemraizer Dec 04 '16 at 22:19
  • @MatíasFidemraizer: If your method is really called `DoStuff` and has no further documentation, then yes, it does violate the LSP (both your first and your second example). If you method is actually called `DoStuffOnLongString` and you somewhere defined that a "long string" is a string with more than 10 characters, then no, the LSP has not been violated (neither in your first nor in your second example). – Heinzi Dec 04 '16 at 22:22
  • My question was originated by this other Q&A: http://stackoverflow.com/a/40957842/411632 See my answer there. @usr raised a concern there: it violates LSP. I'm not saying that it doesn't violate LSP, but I found that it would be interesting to raise a new Q&A to document/analyze the particular case of abstract methods. – Matías Fidemraizer Dec 04 '16 at 22:25
1

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.

fafl
  • 7,222
  • 3
  • 27
  • 50
0

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.

InBetween
  • 32,319
  • 3
  • 50
  • 90