Le principe de substitution de Barbara Liskov (conditions préalables et postconditions)

Pourquoi tant de gens ont-ils des problèmes avec ce principe? Si nous ne prenons pas «abstrus», mais une définition plus simple, alors cela ressemble à ceci:





La classe héritière doit compléter, et non remplacer, le comportement de la classe de base.





Cela semble clair et assez logique, nous ne sommes pas d'accord. mais putain comment y parvenir? Pour une raison quelconque, de nombreuses personnes ignorent simplement les informations sur les conditions préalables et les post - conditions , qui expliquent parfaitement ce qui doit être fait.





Dans cet article, nous ne considérerons PAS d'exemples généraux de ce principe, sur lequel il existe déjà de nombreux matériaux (exemple avec un carré et un rectangle ou des commandes de thermostat ). Ici, nous nous attarderons un peu plus en détail sur des concepts tels que «Préconditions» , «Postconditions» , examinerons ce que sont la covariance, la contravariance et l'invariance , ainsi que ce que sont les «contraintes historiques» ou «règle de l'histoire».





Les conditions préalables ne peuvent pas être renforcées dans une sous-classe

️ En d'autres termes, les classes enfants ne doivent pas créer plus de conditions préalables que celles définies dans la classe de base afin d'effectuer certains comportements commerciaux. Voici un exemple:





<?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



. .





.





, , , , .





Il convient de mentionner qu'il faut s'efforcer de se débarrasser des conditions pré / post. Idéalement, ils devraient être définis comme des paramètres d'entrée / sortie de la méthode (par exemple, en passant des objets de valeur prêts à l'emploi à la signature et en renvoyant un objet valide spécifique à la sortie).





J'espère que cela a été utile.





Sources de

  1. Wiki - Principe de substitution de Barbara Liskov





  2. Metanit





  3. PHP.watch





  4. Chaîne de télégramme, avec de courtes notes








All Articles