Princípios SOLID: o que são e como aplicá-los no PHP/Laravel (Parte 02 - Aberto-fechado)
Tags: php, solid, architecture, laravel
Published: 2020-05-27 23:19:38

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

# Open-closed Principle
_(Princípio Aberto-fechado)_
> Objetos ou entidades devem ser abertas para extensão, mas fechadas para modificação.

Em outras palavras, se eu tenho uma classe genérica que atende a diversos tipos de perfis, ao invés de sair fazer condicionais para cada caso, podemos criar especializações dessa classe, e em cada uma delas eu coloco o que atende aquele perfil.

Vamos continuar o exemplo que eu usei no artigo anterior, onde nós temos 2 tipos de `User`, o `Employee` e o `Contractor`.

Você pode ver [aqui](https://dev.to/lucascavalcante/principios-solid-o-que-sao-e-como-aplica-los-no-php-laravel-parte-01-responsabilidade-unica-3mjj) que eu criei 2 _Repositories_, um para cada tipo de usuário, e em ambas eu criei o método `save()` executando exatamente a mesma coisa. (no final do artigo eu expliquei que usei dessa maneira para facilitar o entendimento da separação de responsabilidades)

Então, vamos melhorar esse código.
```php
class BaseRepository
{
    protected $obj;

    protected function __construct(object $obj)
    {
        $this->obj = $obj;
    }

    public function all(): object
    {
        return $this->obj->all();
    }

    public function find(int $id): object
    {
        return $this->obj->find($id);
    }

    public function findByColumn(string $column, $value): object
    {
        return $this->obj->where($column, $value)->get();
    }

    public function save(array $attributes): bool
    {
        return $this->obj->insert($attributes);
    }

    public function update(int $id, array $attributes): bool
    {
        return $this->obj->find($id)->update($attributes);
    }
}
```
Criei uma classe `BaseRepository` que como o próprio nome diz, serve como base para classes especialistas, contendo os métodos que são genéricos a todas as classes que irão estendê-la.

Vamos criar as classes filhas (ou especialistas).
```php
class EmployeeRepository extends BaseRepository
{
    protected $employee;

    public function __construct(Employee $employee)
    {
        parent::__construct($employee);
    }
}

class ContractorRepository extends BaseRepository
{
    protected $contractor;

    public function __construct(Contractor $contractor)
    {
        parent::__construct($contractor);
    }
}

```
Se as classes `EmployeeRepository` e `ContractorRepository` não tiverem nenhuma especificidade, basta criá-las apenas com o construtor chamando a classe pai que todos os métodos irão funcionar. Na camada de _Service_ quando formos fazer a injeção de dependência, injetamos a classe especialista.

Mas utilizando alguns princípios da orientação a objetos, vamos fazer algo específico para cada uma delas.
```php
class EmployeeRepository extends BaseRepository
{
    protected $employee;

    public function __construct(Employee $employee)
    {
        parent::__construct($employee);
    }

    public function all(): object
    {
        $except = [3,17,22];
        return $this->employee->whereNotIn('id', $except)->get();
    }
}

class ContractorRepository extends BaseRepository
{
    protected $contractor;

    public function __construct(Contractor $contractor)
    {
        parent::__construct($contractor);
    }

    public function save(array $attributes, int $hoursPerWeek): bool
    {
        $attributes['hours_per_week'] = $hoursPerWeek;
        return $this->contractor->insert($attributes);
    }
}
```
Na classe `EmployeeRepository` eu fiz uma sobreposição do método `all()` onde quase tudo é igual à classe pai (nome, parâmetros, retorno) exceto a implementação.

Na classe `ContractorRepository` eu fiz uma sobrecarga do método `save()` onde o nome do método é o mesmo da classe pai, mas a quantidade e/ou tipo dos parâmetros é diferente.

Dessa forma consegui deixar ambas as classes mais especializadas sem interferir na classe pai, ou perder suas características.

Espero que tenha gostado. No próximo artigo da série vou falar sobre o _Liskov Substitution Principle_ (Princípio da Substituição de Liskov)

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