Princípios SOLID: o que são e como aplicá-los no PHP/Laravel (Parte 04 - Segregação de Interface)
Depois de um longo hiato, estou retomando a série dos príncipios SOLID, trazendo o quarto dos princípios.
Interface Segregation Principle
(Princípio da Segregação de Interface)
Uma classe não deve ser forçada a implementar interfaces que não irá utilizar.
Abstração é o coração da orientação a objetos. Em PHP, nós conseguimos alcançar um nível de abstração utilizando interfaces e classes abstratas.
Mas, antes, vamos dar um passo atrás: o que é interface?
Interface é um conjuntos de abstrações que todas as classes que a implementam deve seguir. Cada método adicionado na interface é uma assinatura, e a partir do momento que uma classe a implementa, esta fica obrigada a adicionar todas as assinaturas previamente criadas na interface.
Partindo deste princípio, parece óbvio que uma classe não deva ser forçada a implementar algo que não irá usar. Mas nesse artigo, você verá que este princípio é muito fácil de violar.
Seguindo o nosso exemplo de Employees e Contractors, até o terceiro post da série eu criei uma classe chamada UserService
que injeta os repositories para os dois tipos de usuário.
Vamos fazer uma pequena alteração, a partir de agora será um service para cada tipo de usuário.
class EmployeeService
{
protected $employeeRepository;
public function __construct(EmployeeRepository $employeeRepository)
{
$this->userRepository = $userRepository;
}
}
class ContractorService
{
protected $contractorRepository;
public function __construct(ContractorRepository $contractorRepository)
{
$this->contractorRepository = $contractorRepository;
}
}
Dessa maneira eu terei duas classes que irão implementar a mesma interface UserInterface
.
interface UserInterface
{
public function register(array $attributes, UserRepository $userRepository);
public function calcWorkedHours(array $hours);
}
Interface criada com a assinatura do método register
(que foi criado nos posts anteriores), e agora adicionei o método calcWorkedHours
que será um método útil para o usuário do tipo Contractor onde as horas dele serão calculadas para gerar o pagamento no fim do mês.
Vamos implementar a interface nos dois services.
class EmployeeService implements UserInterface
{
public function register(array $attributes, UserRepository $userRepository)
{
// implementação do código
}
}
class ContractorService implements UserInterface
{
public function register(array $attributes, UserRepository $userRepository)
{
// implementação do código
}
public function calcWorkedHours(array $hours)
{
// implementação do código
}
}
Dessa maneira nós vamos ter um erro na classe EmployeeService
pois ela implementa a UserInterface
mas não usa o método calcWorkedHours
.
Uma maneira errada de resolver isso seria implementar o método, mas sem nenhuma implementação dentro do escopo.
A maneira correta seria criar duas interfaces e implementá-las onde for necessário.
interface UserInterface
{
public function register(array $attributes, UserRepository $userRepository);
}
interface ContractorInterface
{
public function calcWorkedHours(array $hours);
}
E após criar as duas interfaces, as classes já poderiam escolher quem implementar.
class EmployeeService implements UserInterface
{
public function register(array $attributes, UserRepository $userRepository)
{
// implementação do código
}
}
class ContractorService implements UserInterface, ContractorInterface
{
public function register(array $attributes, UserRepository $userRepository)
{
// implementação do código
}
public function calcWorkedHours(array $hours)
{
// implementação do código
}
}
Dessa maneira, não obrigamos nenhuma classe a implementar algo que não irá utilizar. Sendo assim, não violamos o princípio da segregação de interface. :)
Quando tentamos entender apenas a teoria dos princípios, parecem bem mais complicados do que realmente são.
No próximo, e último, artigo da série vou falar sobre o Dependency Inversion Principle (Princípio da Inversão de Dependência).
Dúvidas e feedbacks são sempre bem-vindos.