Os princípios SOLID — LSP

Liskov substitution principle (LSP) — Princípio da substituição de Liskov

Felippe Roberto Bayestorff Duarte
3 min readFeb 17, 2018

Continuando do artigo anterior, vamos falar agora sobre o terceiro princípio, LSP.

O LSP, princípio da substituição de Liskov, diz que “Seja φ(x) é uma propriedade demonstrável dos objetos x de tipo T. Então φ(y) deve ser verdadeiro para objetos y de tipo S onde S é um subtipo de T.”

Legal, não entendi nada.

Vamos com calma. Este princípio, criada pela cientista da computação Bárbara Liskov (em um paper feito em conjunto com Jeanette Wing), diz, traduzindo em nas palavras do Uncle Bob: “Funções que usam ponteiros ou referências à classes bases devem ser capazes de usar objetos de classes derivadas sem saber disso”.

Tentando explicar de forma mais simples, sempre que você criar uma classe filha, o utilizador dessa classe deve ser capaz de substituir pela classe pai sem afetar o código.

Talk is cheap, show me the code

Vamos pegar um exemplo comum de muitas aplicações, a classe Pessoa, que deriva para PessoaFisica e PessoaJuridica

class Pessoa
{
protected $nome;
protected $dataNascimento;
public function getNome()
{
return $this->nome;
}
public function getDataNascimento()
{
return $this->dataNascimento;
}
}
class PessoaFisica extends Pessoa
{
protected $rg;
protected $cpf;
public function getRg()
{
return $this->rg;
}
public function getCpf()
{
return $this->cpf;
}
}
class PessoaJuridica extends Pessoa
{
protected $inscricaoEstadual;
protected $cnpj;
public function getInscricaoEstadual()
{
return $this->inscricaoEstadual;
}
public function getCnpj()
{
return $this->cnpj;
}
}

Esse é um modelo visto em muitas aplicações. Mas qual a consequência disso? A incapacidade de abstrair para a classe Pessoa e obrigar o cliente a saber qual a instância de pessoa. No caso de fazer o código acima, o único motivo, via código, de criar a herança é apenas para reaproveitar alguns campos. Veja o uso da classe a seguir:

$pessoa = new Pessoa();
if($pessoa instanceOf PessoaFisica) {
$documento = $pessoa->getCpf();
} else {
$documento = $pessoa->getCnpj();
}

Percebe o problema? A cada tipo de pessoa, a cadeia if-else aumenta indefinidamente. Dependendo da aplicação, a classe pessoa se especializa em cliente, funcionário e por aí vai. Esse exemplo é simplificado, mas o caso é que uma Pessoa Física é diferente de uma Pessoa Jurídica em muitos aspectos, e o uso de herança apenas para reaproveitamento de código pode levar a problemas indesejados e constantes verificações.

No fim das contas, o “cliente” (quem usa a classe) precisa sempre saber com qual instância está trabalhando.

Mas qual seria então a melhor forma de trabalhar com esse tipo de caso?

Bom, uma solução possível aqui seria implementar uma interface comum para ambas, que atenda aos cenários comuns, e deixar os métodos especialistas para os casos onde se sabe qual instância está sendo operada.

O ganho disso vai depender da aplicação, mas a título de exemplo, segue um modelo possível:

<?phpabstract class Pessoa
{
protected $nome;
protected $dataNascimento;
public function getNome()
{
return $this->nome;
}
public function getDataNascimento()
{
return $this->dataNascimento;
}
abstract public function getDocumento();
}
class PessoaFisica extends Pessoa
{
protected $rg;
protected $cpf;
public function getRg()
{
return $this->rg;
}
public function getDocumento()
{
return $this->cpf;
}
}
class PessoaJuridica extends Pessoa
{
protected $inscricaoEstadual;
protected $cnpj;
public function getInscricaoEstadual()
{
return $this->inscricaoEstadual;
}
public function getDocumento()
{
return $this->cnpj;
}
}
class Parceiros
{
public function buscaParceiro(Pessoa $pessoa)
{
$doc = $pessoa->getDocumento();
\DB::table('parceiros')->where('doc','=', $doc)->get();
...
}
}

No exemplo acima, a classe Parceiros utiliza uma instância de Pessoa . Se mudarmos por PessoaFisica ou PessoaJuridica o código continuará funcionando, pois ambas as classes implementam o método getDocumento() e retornam um valor adequado à sua especialização.

Claro, este é um exemplo simplificado e talvez não seja aderente à sua realidade, mas o importante é notar os problemas que a violação do LSP acarretam e buscar uma alternativa que mantenha o reuso do código porém sem violar a regra.

Espero que tenham gostado. O próximo artigo será sobre o ISP - Princípio da segregação de interface.

Os códigos estão no repositório: https://github.com/felippeduarte/medium/tree/master/solid

--

--