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 em sua apresentação "Data Abstraction" 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 eu criei 2 repositories para ambos os perfis de usuário, e os injetei como dependência no UserService
.
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, 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.
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
.
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.
Top comments (7)
Parabéns Lucas, muito bem explicado, no aguardo da continuação dessa série!
Passei um tempo ausente da geração de conteúdo, mas estou retomando. :) Logo, logo terá post novo na continuação da série.
Feito! :)
dev.to/lucascavalcante/principios-...
Legal, mas não é uma boa prática o objeto Request sair do controller. Request é da camada http, um service autônomo teria que implementar um request para chamar esse Repository 😅.
Fala Douglas! Tudo bem?
Você está coberto de razão. Eu sou um grande defensor de se manter
requests
eresponses
apenas no controller, já que essa é a principal responsabilidade dessa camada.A maneira como implementei aí no exemplo foi para facilitar a didática, pois se tivesse que implementar um
Request
ou, ainda, fazer essa validação no próprioController
, traria uma complexidade ao exemplo.Mas, vou ficar mais atento para não ferir boas práticas, independente da didática abordada.
Obrigado pelo feedback.
Muito simples a didática, espero que continue a série.
Feito! :)
dev.to/lucascavalcante/principios-...