O princípio de substituição de Barbara Liskov (pré-condições e pós-condições)

Por que tantos têm problemas com esse princípio? Se tomarmos não "obscuro", mas uma definição mais simples, então soa assim:





A classe herdada deve complementar, não substituir, o comportamento da classe base.





Parece claro e bastante lógico, discordamos. mas como conseguir isso? Por alguma razão, muitas pessoas simplesmente ignoram as informações sobre pré - condições e pós - condições , que explicam perfeitamente o que precisa ser feito.





Neste artigo, NÃO vamos considerar exemplos gerais deste princípio, sobre o qual já existem muitos materiais (exemplo com um quadrado e um retângulo ou controles de termostato ). Aqui nos deteremos com um pouco mais de detalhes em conceitos como "Pré-condições" , "Pós-condições" , considerar o que são covariância, contravariância e invariância , bem como o que são "restrições históricas" ou "regra da história".





As pré-condições não podem ser reforçadas em uma subclasse

️ Em outras palavras, as classes filhas não devem criar mais pré-condições do que as definidas na classe base para realizar algum comportamento de negócios. Aqui está um exemplo:





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



. .





.





, , , , .





Vale ressaltar que devemos nos esforçar para nos livrarmos das condições pré / pós. Idealmente, eles devem ser definidos como parâmetros de entrada / saída do método (por exemplo, passando objetos de valor prontos para a assinatura e retornando um objeto válido específico para a saída).





Espero que tenha sido útil.





Fontes de

  1. Wiki - Princípio de Substituição de Barbara Liskov





  2. Metanit





  3. PHP.watch





  4. Canal Telegram, com notas curtas








All Articles