Princípios SOLID: o que são e como aplicá-los no PHP/Laravel (Parte 03 - Substituição de Liskov)
Tags: php, solid, architecture, laravel
Published: 2020-06-15 22:56:37

Dando continuidade à série de artigos sobre princípios SOLID, hoje vou falar sobre o terceiro dos princípios.

# Liskov Substitution Principle
_(Princípio da Substituição de Liskov)_

> Uma classe derivada deve ser substituída por sua classe base.

Este princípio tem este nome porque foi introduzido por [Barbara Liskov](https://pt.wikipedia.org/wiki/Barbara_Liskov) em sua apresentação "[Data Abstraction](https://klevas.mif.vu.lt/~plukas/resources/OODPrinciples/Liskov1987.pdf)" em 1987. Alguns anos mais tarde, ela publicou um artigo, junto com Jeanette Wing, onde elas definiram o princípio como:

> Seja Φ(x) uma provável propriedade sobre objetos x do tipo T. Então Φ(y) deve ser verdadeira para objetos y do tipo S, em que S é um subtipo de T.

Uma definição um tanto quanto complexa, não é mesmo? Normal, quando se trata de um artigo científico. Vamos simplificar e ver como isso se aplica na prática.

Continuando o nosso exemplo de _Employees_ e _Contractors_, no [primeiro artigo dessa série](https://dev.to/lucascavalcante/principios-solid-o-que-sao-e-como-aplica-los-no-php-laravel-parte-01-responsabilidade-unica-3mjj) eu criei 2 _repositories_ para ambos os perfis de usuário, e os injetei como dependência no `UserService`.

```php
class UserService
{
    protected $employeeRepository;
    protected $contractorRepository;

    public function __construct(
        EmployeeRepository $employeeRepository,
        ContractorRepository $contractorRepository
    )
    {
        $this->userRepository = $userRepository;
        $this->contractorRepository = $contractorRepository;
    }

    public function register(Request $request)
    {
        $attributes = $request->all();
        if($attributes['type'] === 'employee') {
            $this->employeeRepository->save($attributes);
        }

        if($attributes['type'] === 'contractor') {
            $this->contractorRepository->save($attributes);
        }
    }
}
```
No [segundo artigo da série](https://dev.to/lucascavalcante/principios-solid-o-que-sao-e-como-aplica-los-no-php-laravel-parte-02-aberto-fechado-4ddp), eu criei um arquivo `BaseRepository` que seria a classe pai de `EmployeeRepository` e `ContractorRepository`.

É muito simples aplicar a substituição de Liskov.

Primeiro vamos criar um _repository_ intermediário entre a classe pai e as classes filhas.

```php
class BaseRepository
{
    // seu código
}

class UserRepository extends BaseRepository
{
    // seu código
}

class EmployeeRepository extends UserRepository
{
    // seu código
}

class ContractorRepository extends UserRepository
{
    // seu código
}
```

Dessa forma, a `BaseRepository` continua sendo "base" para outros _repositories_ também, e a `UserRepository` vira a classe pai das outras duas.

Agora vamos fazer uma pequena alteração no classe `UserService`.

```php
class UserService
{
    protected $employeeRepository;
    protected $contractorRepository;

    public function __construct(
        EmployeeRepository $employeeRepository,
        ContractorRepository $contractorRepository
    )
    {
        $this->userRepository = $userRepository;
        $this->contractorRepository = $contractorRepository;
    }

    public function checkUserType(Request $request)
    {
        $attributes = $request->all();
        if($attributes['type'] === 'employee') {
            $this->register($attributes, $this->employeeRepository);
        }

        if($attributes['type'] === 'contractor') {
            $this->register($attributes, $this->contractorRepository);
        }
    }

    public function register(array $attributes, UserRepository $userRepository)
    {
        $userRepository->save($attributes);
    }
}
```
Renomeei o método `register()` para `checkUserType()` onde continua sendo feita a validação do tipo de usuário. Mas, ao invés de chamar diretamente o _repository_, agora chama outro método dentro desta mesma classe, que por sua vez, agora se chama `register()` e recebe como parâmetro o _array_ `$attributes` e uma instância de `UserRepository`.

Independentemente de qual dos _repositories_ for passado como parâmetro, ambos são do tipo `UserRepository` e quando for chamado o método `save()`, irá chamar de acordo com o que foi passado.

Dessa forma, nós temos a chamada para o _repository_ de uma forma mais centralizada, e mantendo o mesmo tipo de objeto na passagem do parâmetro.

Espero que tenha ficado claro para você. Caso contrário, comente aí sua dúvida.

E aí, gostou? No próximo artigo da série vou falar sobre o Interface Segregation Principle (Princípio da Segregação de Interface)

Dúvidas e feedbacks são sempre bem-vindos.