Este artigo é a parte 5 de 5 na série SOLID

O Princípio da Segregação de Interface (Interface Segregation Principle, ou ISP) é o quarto dos cinco princípios SOLID e foca em garantir que as interfaces sejam coesas e que as classes não sejam forçadas a depender de métodos que não utilizam. Ele afirma que:

nenhum cliente deve ser forçado a depender de métodos que ele não usa.

Em outras palavras, é preferível ter múltiplas interfaces menores e bem definidas, em vez de uma única interface grande que agregue responsabilidades demais.

O Problema da Interface “Gorda”

Uma interface gorda (ou “inchada”) é uma interface que define métodos que nem todas as classes que a implementam realmente necessitam. Isso leva a classes que têm que fornecer implementações para métodos que não fazem sentido no seu contexto, resultando em código desnecessário, menos coeso e mais difícil de manter.

O princípio da Segregação de Interface visa evitar esse problema ao incentivar o uso de interfaces mais específicas, que sejam diretamente relacionadas às responsabilidades de uma classe.

Problemas de Não Seguir o ISP

  1. Implementações Vazias ou Inúteis: Se uma classe forçada a implementar um método de uma interface que não faz sentido para ela, acaba tendo que fornecer implementações vazias ou que não fazem nada relevante.
  2. Acoplamento Desnecessário: Quando uma classe depende de métodos que não precisa, acaba criando um acoplamento desnecessário, aumentando a complexidade do sistema e tornando o código mais difícil de entender e manter.
  3. Baixa Reutilização: Interfaces inchadas tendem a ser menos reutilizáveis, pois nem todas as classes precisam de toda a funcionalidade descrita por elas.

Exemplo Prático em Dart

Vamos imaginar um cenário onde temos uma interface Worker que descreve as responsabilidades de um trabalhador em uma empresa:

Interface que Viola o ISP:

abstract class Worker {
  void work();
  void attendMeeting();
  void manageTeam();
}

class Developer implements Worker {
  @override
  void work() {
    print('Desenvolvendo código...');
  }

  @override
  void attendMeeting() {
    print('Participando de uma reunião...');
  }

  @override
  void manageTeam() {
    // Desenvolvedor não gerencia uma equipe
    throw UnsupportedError('Developers cannot manage a team');
  }
}

No exemplo acima, a interface Worker define três responsabilidades: trabalhar, participar de reuniões e gerenciar equipe. No entanto, um desenvolvedor pode não ter a responsabilidade de gerenciar equipe, e a implementação desse método na classe Developer gera uma exceção ou uma implementação vazia, o que é um sinal claro de violação do ISP.

Refatorando para Seguir o ISP:

Podemos dividir a interface Worker em múltiplas interfaces menores, mais coesas, que garantem que cada classe implemente apenas os métodos que fazem sentido para ela:

abstract class Workable {
  void work();
}

abstract class Attendee {
  void attendMeeting();
}

abstract class TeamManager {
  void manageTeam();
}

class Developer implements Workable, Attendee {
  @override
  void work() {
    print('Desenvolvendo código...');
  }

  @override
  void attendMeeting() {
    print('Participando de uma reunião...');
  }
}

class Manager implements Workable, Attendee, TeamManager {
  @override
  void work() {
    print('Trabalhando em táticas e estratégias...');
  }

  @override
  void attendMeeting() {
    print('Participando de uma reunião...');
  }

  @override
  void manageTeam() {
    print('Gerenciando a equipe...');
  }
}

No exemplo acima, dividimos a interface Worker em três interfaces menores:

  • Workable: Define o método work(), que descreve a responsabilidade de realizar trabalho.
  • Attendee: Define o método attendMeeting(), que descreve a participação em reuniões.
  • TeamManager: Define o método manageTeam(), que descreve o gerenciamento de uma equipe.

Dessa forma, a classe Developer implementa apenas as interfaces que fazem sentido para ela (Workable e Attendee), enquanto a classe Manager implementa todas as interfaces que descrevem suas responsabilidades. Isso torna o código mais limpo, mais coeso e mais aderente ao princípio de Segregação de Interface.

Benefícios do Princípio da Segregação de Interface

  1. Classes Mais Coesas: As classes ficam focadas em suas responsabilidades reais, sem métodos desnecessários.
  2. Reutilização Aprimorada: Interfaces menores e bem definidas são mais fáceis de serem reutilizadas em outros contextos.
  3. Redução do Acoplamento: Como as classes não precisam implementar métodos que não utilizam, o acoplamento é reduzido, facilitando a manutenção e a evolução do sistema.
  4. Facilidade de Teste: Classes e interfaces menores são mais fáceis de testar, pois há menos funcionalidades para serem cobertas em cada unidade.

Resumo

O Princípio da Segregação de Interface nos ensina a evitar interfaces inchadas que forçam classes a implementar métodos desnecessários. Ao dividir interfaces grandes em múltiplas interfaces menores e coesas, podemos criar sistemas mais limpos, modulares e fáceis de manter. Isso resulta em um código mais flexível e que respeita o princípio de responsabilidade única, onde cada classe e interface foca apenas nas suas próprias responsabilidades.

Seguir o ISP é essencial para garantir que seu código permaneça simples, fácil de modificar e adaptável às mudanças nos requisitos do negócio ao longo do tempo, contribuindo para um design de software mais robusto e elegante.