3
respostas

Liskov Substitution Principle - LSP

Olá!

Confesso que não entendi o LSP.

Por favor, se for prover um exemplo, que não seja do "Square-Rectangle". =]

Grato.

3 respostas

Oi Marcelo, tudo bom?

A ideia do LSP é garantir que as regras de negocio da classe mãe nunca sejam quebradas pelas classes filhas.

O exemplo Square-Rectangle é o mais comum mesmo. Entretanto, se pensarmos, por exemplo, em uma Conta que apenas realiza um saque e um deposito:

class Conta {
    private $saldo;

    public function deposita(float $valor) {
        if($valor >= 1.0)
            $this->saldo += $valor
        else
            throw new InvalidArgumentException("Apenas depositos acima de um real são permitidos. ");
    }
    public function saca(float $valor) {
            $this->saldo -= $valor;
    }
}

E uma classe ContaCorrente, que é filha de Conta e sobrescreve a forma de sacar e depositar:

class ContaCorrente {
    private $saldo;

    public function saca(float $valor) {
        if($valor >= $this->saldo)
            $this->saldo -= $valor;
        else
            throw new InvalidArgumentException("Saldo insuficiente");
    }

    public function deposita(float $valor) {
        $this->saldo+= $valor;
    }
}

Se tentarmos realizar um saque em uma conta:

$conta = new Conta();
$conta->deposita(100.0);
$conta->saca(200.0);

Funciona normalmente.

Mas, se tentarmos o mesmo com ContaCorrente:

$conta = new ContaCorrente();
$conta->deposita(100.0);
$conta->saca(200.0); // lança InvalidArgumentException

Temos um erro. Isso significa que uma ContaCorrente não consegue realizar as mesmas funcionalidades que a classe mãe dela, Conta.

Aqui estamos quebrando o principio de substituição de Liskov. As pré condições do saque de uma ContaCorrente são mais fechados que as de uma Conta, para o método saca.

Uma referência poderia ser feita também com o método deposita.

Se tentarmos deposita de uma Conta comum:

$conta = new Conta();
$conta->deposita(0.5); // Lança exception

Recebemos o erro.

Mas, se tentarmos com uma ContaCorrente:

$conta = new ContaCorrente();
$conta->deposita(0.5);

Executa normalmente!

Isso significa que após executar o método deposita com um valor menor que um em uma ContaCorrente, temos o saldo atualizado. O mesmo não ocorre com uma Conta!

Aqui estamos quebrando, também, o principio de substituição. As pós condições do deposita de uma ContaCorrente são mais abertos que as de uma Conta, para ao método deposita.

Espero que o exemplo tenha ficado um pouco mais claro.

Esse conceito é bem abstrato mesmo é normal ficar meio confuso.

Uma abordagem que ajuda bastante é não pensar diretamente nas pré condições e pós condições que são os conceitos mais teoricos da coisa. Mas, sim, olhar para a ideia de substituição mesmo. Garantir que todo comportamento da classe mãe não seja desrespeitado nas classes filhas de forma alguma. Assim, sempre vamos conseguir substituir uma instancia da classe mãe por qualquer instancia de classes filhas sem precisar alterar o código. Ou seja, garantimos uma melhor manutenção pro sistema e mais coerencia pra nossa modelagem.

Abraço e bons estudos.

Poxa André, obrigado pela resposta e explicação, mas, na minha concepção, de Clean Code, este pedaço de código para a função saca()

if($valor >= $this->saldo)
            $this->saldo -= $valor;
        else
            throw new InvalidArgumentException("Saldo insuficiente");

já deveria estar era na conta mãe! na super classe e não esperar para colocá-lo apenas na corrente pela própria definição do que é uma conta, que não permite saques além do limite ou depósitos negativos. Sendo esta segunda situtação, até uma inconsistência lógica.

Portanto, não haveria um outro exemplo ?

Muito obrigado desde já.

Marcelo, acho que você não deveria se preocupar com esse detalhe lógico. O André provavelmente apenas quis dar um exemplo de quebra de pré e pós-condições qualquer para exemplificar. Se isso dificulta o seu entendimento tente pensar em outra condição restritiva qualquer que aconteceria em uma conta normal e uma conta corrente ao sacar e depositar.

[]s