Princípios SOLID: o que são e como aplicá-los no PHP/Laravel (Parte 04 - Segregação de Interface)

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.