2

I have some doubts about the practical way of violate or to not the pre and post conditions based on Liskov Substitution Principle.

In the beginning, I have create the examples where first child would respect and the second to violate. But Liskov article is very mathematical, and now, I do not have more confidence about it.

First about pre:

<?php

class TermCalculator {
    public function data(int $dias): DateTimeInterface {
        if($dias > 0)
            return (new DateTime())->modify("+$dias days");
        throw new \InvalidArgumentException("Term needs to be above zero ");
    }
}
class TermCalculatorCLT extends TermCalculator {
    public function data(int $dias): DateTimeInterface {
        if ($dias >= 0)
            return (new DateTime())->modify("+$dias days");
        throw new \InvalidArgumentException("Term needs to be above -1");
    }
}
class TermCalculatorCPC extends TermCalculator {
    public function data(int $dias): DateTimeInterface {
        if (in_array($dias, range(1,30)))
            return (new DateTime())->modify("+$dias days");
        throw new \InvalidArgumentException("Term needs to be between 1 and 30 ");
    }
}

**

  • Which children class above violates pre-condition?

**

And two situations about post:

<?php
class Account {
    protected float $balance;
    public function __construct(float $balanceInicial){
        $this->balance = $balanceInicial;   
    }
    public function withdraw(float $value) : float { 
        if(($this->balance - $value) >= 0)
          $this->balance -= $value;
        return $this->balance;
    }
}
class AccountVip extends Account {
    private const TAXA = 10.00;
    public function withdraw(float $value) : float {
        if(($this->balance - $value) >= self::TAXA)
            $this->balance -= $value;
        return $this->balance;
    }
}
class AccountIlimited extends Account {
    public function withdraw(float $value) : float {
        $this->balance -= $value;
        return $this->balance;
    }
}

**

  • Which children class above violates post-condition?

**

4

The Liskov Substitution Principle (LSP) requires that all the child could be used interchangeably with their parents.

This means concretely that the child must work with the tests prepared for the parents. So if you have any pre-conditions for your parents, the child shall not strengthen them. Conversely, the child cannot weaken the post-conditions of the parent.

In your case:

  • TermCalculator has a precondition of $dias>0. It the precondition is not met, it throws.
  • TermCalculatorCLT has a precondition of $dias>=0. It's weaker than the precodintion of the parent, because it accepts the special case of $dias being 0 while the parent not. Weakening the parent conditions means that it doesn't strengthen them. So this is compliant with LSP.
  • TermCalculatorCPC has a predindition of $dias>0 and $dias<31. It's stronger than the precondition of the parent (i.e. more selective). Take the example of 32: it would work for the parent, but it would throw for the child. This means that the child might not pass all the tests of the parent. Tehrefore it doesn't respect LSP.

I leave you as an excercise the second case. If it's really to difficult, you may post as comment to this answer your guess and explanations of your choice, and I'd edit my answer accordingly ;-)

| improve this answer | |
  • This means concretely that the child must work with the tests prepared for the parents - simple and easy to check – Fabio Jan 29 at 20:21
  • I think having a child class that can work in all tests written for the parent is a necessary but not sufficient condition for LSP compliance. If the parent guarantees immutability or some other form of change constraint (e.g. a list that can only be appended to) the child must respect that. The test for the parent won't have a call to $list->removeItem() since that doesn't exist on the parent. If it does exist on the child it's an LSP violation. – bdsl Jan 29 at 23:08
  • @bdsl I restricted my answer to the specific case of the questiin. Liskov’s argument is indeed not about passing the tests of the it’s parent but about being substitute for their parents. This means that invarients must be respected, that postconditions are not weakened (eg immutable for the parent requires immutable for the child. Last but bot least the child shall respect the history rule. – Christophe Jan 29 at 23:28
  • Thanks @Christophe, so Can we say that AccountVip has a stronger post-condition, because returns only >= TAXA and so is valid for LSP and, in other hand, AccountIlimited can returns negative number, weaking the original post-condition (only positive numbers), violating LSP, right? – celsowm Jan 30 at 0:41
  • @celsowm yes, that’s the principle. Assuming that the post condition is that balance must be positive or null. Note that in the first case, the precondition is coded (exception raised). But in the second case there is not such a post condition check. So we guessed it for the exercise. Do you have a test case that states that balance should be positive? in real life there is not necessarily a postcondition here. LSP is about contracts, i.e. promises made about the code and not the code itself. If postcondition would be that new balance is equal or smaler than initial balance, all classes comply – Christophe Jan 30 at 7:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.