The Barbara Liskov Substitution Principle (Preconditions and Postconditions)

Why do so many have problems with this principle? If we take not "abstruse", but a simpler definition, then it sounds like this:





The inheriting class should complement, not override, the behavior of the base class.





It sounds clear and quite logical, we disagree. but damn how to achieve this? For some reason, many people simply skip information about preconditions and postconditions , which just perfectly explain what needs to be done.





In this article, we will NOT consider general examples of this principle, about which there are already many materials (example with a square and a rectangle or thermostat controls ). Here we will dwell a little more on such concepts as "Preconditions" , "Postconditions" , consider what covariance, contravariance and invariance are , as well as what "historical constraints" or "rule of history" are.





Preconditions cannot be strengthened in a subclass

️In other words, child classes should not create more preconditions than defined in the base class in order to perform some business behavior. Here's an example:





<?php

class Customer
{
    protected float $account = 0;

    public function putMoneyIntoAccount(int|float $sum): void
    {
        if ($sum < 1) {
            throw new Exception('       1$');
        }

        $this->account += $sum;
    }
}

class  MicroCustomer extends Customer
{
    public function putMoneyIntoAccount(int|float $sum): void
    {
        if ($sum < 1) {
            throw new Exception('       1$');
        }

        //  
        if ($sum > 100) { 
            throw new Exception('      100$');
        }

        $this->account += $sum;
    }
}
      
      



​ . !





«», , .





, , .





, , Bar->process()



, .





<?php

class Foo
{
    public function process(int|float $value)
    {
       // some code
    }
}

class Bar extends Foo
{
    public function process(int|float|string $value)
    {
        // some code
    }
}
      
      



, VIPCustomer



putMoneyIntoAccount



( ) Money



, ( Dollars



).





<?php

class Money {}
class Dollars extends Money {}

class Customer
{
    protected Money $account;

    public function putMoneyIntoAccount(Dollars $sum): void
    {
        $this->account = $sum;
    }
}

class VIPCustomer extends Customer
{
    public function putMoneyIntoAccount(Money $sum): void
    {
        $this->account = $sum;
    }
}
      
      



, , .





​️ , . .





<?php

class Customer
{
    protected Dollars $account;

    public function chargeMoney(Dollars $sum): float
    {
        $result = $this->account - $sum->getAmount();

        if ($result < 0) { // 
            throw new Exception();
        }

        return $result;
    }
}

class  VIPCustomer extends Customer
{
    public function chargeMoney(Dollars $sum): float
    {
        $result = $this->account - $sum->getAmount();

        if ($sum < 1000) { //   
            $result -= 5;  
        }
       
        //    
      
        return $result;
    }
}
      
      



​ , . !





- «», (?!), .





. render()



, JpgImage



, Image



, Renderer



.





<?php

class Image {}
class JpgImage extends Image {}

class Renderer
{
    public function render(): Image
    {
    }
}

class PhotoRenderer extends Renderer
{
    public function render(): JpgImage
    {
    }
}
      
      



​️ . . :)





.





- .





β€” , . , .





.





<?php 

class Wallet
{
    protected float $amount;
    //        
}
      
      



(Β« Β»):





.





, . , . 





<?php

class Deposit
{
    protected float $account = 0;

    public function __construct(float $sum)
    {
        if ($sum < 0) {
            throw new Exception('      ');
        }

        $this->account += $sum;
    }
}

class VipDeposit extends Deposit
{
    public function getMoney(float $sum)
    {
        $this->account -= $sum;
    }
}
      
      



​ Deposit



. VipDeposit



, account



, Deposit



. .





.





, , , , .





It is worth mentioning to strive to get rid of pre / post conditions. Ideally, they should be defined as input / output parameters of the method (for example, by passing ready-made value objects to the signature and returning a specific valid object to the output).





Hope it was helpful.





Sources of

  1. Wiki - Barbara Liskov Substitution Principle





  2. Metanit





  3. PHP.watch





  4. Telegram channel, with short notes








All Articles