Is it possible to weaken the premises and strengthen the postconditions, also violate the principle of Liskov substitution?

The actual subtype precondition is created by combining (using logical OR ) the base type preconditions and the subtype preconditions, which makes the resulting precondition less restrictive

The actual postcondition of the subtype is created by combining (using logical AND ) postconditions of the base type and postconditions of the subtype, which makes the resulting postcondition more restrictive

The following are examples of simplifying preconditions and easing postconditions that as a result violate the LSP ( Link ):

  • Suppose your base class works with an int element. Now your subtype requires int to be positive. This is a reinforcement of preconditions, and now any code that worked fine with negative ints.

  • Similarly, suppose the same scenario, but the base class, is used to guarantee that the member will be positive after it is called. then the subtype modifies the behavior to allow negative ints. The code that works on the object (and assumes that the post-condition is a positive int) is now violated, since the post-condition is not supported.

a) Why is this also not considered a violation of the LSP when an overridden method relaxes the precondition, since this method may use parameters that are unacceptable for basic contracts. Thus, we cannot claim that the base type contract was violated, and as a result, the LSP was also violated?

b) Why is this also not considered a violation of the LSP when an overridden method reinforces the postcondition, since clients calling this method will receive only a subset of the possible results of the original method. Thus, we cannot claim that the base type contract was violated, and as a result, the LSP was also violated?

Example:

The postcondition of the base class ensures that the return value of the method is within the range of 1-10 , but then the subtype changes the postcondition to allow only the return value in the range of 2-9 . Now the code that works with the object returned using this method (and assumes that post-existence is within the range of 1-10 ) is broken, because the postcondition is not supported.

+7
source share
5 answers

Sorry, but you have a logical error in your thoughts.

The postcondition of the base class ensures that the return value of the method will be in the range 1-10, but then the subtype modifies the postcondition to allow only the return value within the range of 2-9.

Since the code works in the range of 1-10, and the range of 2-9 is actually in the range of 1-10, gain , post-privacy will never be a problem.

Same thing with weakening premises. Allowing a subtype to accept a larger range does not violate the behavior of the base type. Since the behavior is introduced only in the subtype and only as a prerequisite for subtype methods.

0
source

I think your example does not support your point in the following sense.

In a negative int, a positive int is an example, adding negative numbers, although it includes more features, weakens the post-state guarantee. An example of your example does the same. Although it guarantees a tighter range, it still weakens the post-state guarantee provided by the base class.

The post-state rule actually says:

This rule says that the subtype method provides more than the supertype method: when it returns everything that the supertype method provides, and possibly some additional effects (programming in Java, p177)

Your example does not guarantee everything that guarantees a supertype. I think in your example, reinforcing would mean that the subtype guarantees that the returned int is in 1-10, in addition, it ensures that the returned int is between 2-9, and not just the second.

-one
source

If the base class contract for a method indicates that calling it with a negative number will throw an exception, then any legitimate derived class should throw the same exception if its method is passed a negative number. If, however, the base class contract simply says that the method cannot be called with negative numbers without specifying what will happen if it is, then the base class could do whatever it likes without breaking this contract.

While it may seem ridiculous that the behavior of the class will be unspecified, adding functions to the class, while maintaining compatibility with the upstream, usually requires changing what was the undefined behavior in the specified ones. In some cases, the “unspecified” behavior in the class may not be compiled, but there is no guarantee that the code trying to use such an element will always not be compiled. For example, if the "Foo" member is not mentioned in the class contract, an attempt to use such a member will most likely cause a compilation error, but a future version of the class may define Foo without violating its contract.

-one
source

LSP means you must substitute the base class with a subclass for the input values ​​in the specification. It makes no sense to compare the behavior of a base class with a subclass when you break the base class contract in the first place (you are not dealing with true substitution if you do this).

(Breach of contract is an unknown behavior, it just happens that the most common approach is throwing an exception.)

Regarding the strengthening of the postcondition, I do not see your point, really (?) If the contract values ​​are 1-10, any values ​​between 1-10 are de facto in the specification, also 2-9 or even 3 always (?)

-one
source

You are absolutely right. The premise cannot be relaxed. This would also change the behavior of the base type. For example:

 class Base { void method(int x) { /* x: 1-100 allowed else exception */ } } class Weak: Base { void method(int x) { /* x: 1-1000 allowed else exception */ } } class Strong: Base { void method(int x) { /* x: 1-10 allowed else exception */ } } int Main() { Base base = new Base(); base.method(101-1000); // exception Base base2 = new Weak(); base2.method(101-1000); // ok Base base3 = new Strong(); base3.method(101-1000); // exception } 

LSP is clearly broken: the weak class for 101-1000 is normal, but the base class for the exception is 101-1000 exceptions. This is clearly not the same behavior.

The LSP expects that the set of numbers for the base class can be expanded (strengthened) in subclasses, but in the main program the set will not be expanded, and therefore a subclass with weaker prerequisites can completely fill the preconditions of the base class.

The same applies to postconditions in a different way.

-2
source

All Articles