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.
Top comments (5)
Mais uma ótima explicação.
Uma dúvida: a classe BaseRepository poderia ser Abstrata ?
Olá, Robson! Desculpe a demora para responder... por algum motivo, não recebi essa notificação.
Poderia ser abstrata sim, sem nenhum problema. Mas como eu quis focar aqui na questão da extensibilidade de uma classe, sem necessariamente falar de assinaturas de contratos de classes abstratas e/ou interfaces, aí acabei usando uma herança simples mesmo.
Obrigado pelo comentário. :)
Ótimo conteúdo Lucas!
Muito bom, Lucas! Aguardando os próximos artigos.
Conteúdo de ótima qualidade, isso abriu minha mente de um jeito incrível, muito obrigado Lucas por compartilhar um conteúdo tão importante