Princípios SOLID: o que são e como aplicá-los no PHP/Laravel (Parte 02 - Aberto-fechado)

Princípios SOLID: o que são e como aplicá-los no PHP/Laravel (Parte 02 - Aberto-fechado)

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

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).

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.

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.