ISP - Interface Segregation Principle
Definição formal
Seção intitulada “Definição formal”Robert C. Martin enuncia: “Clients should not be forced to depend upon interfaces that they do not use.” O princípio nasceu de um problema real na Xerox, onde uma classe Job monolítica servia a diversos clientes, e qualquer mudança forçava recompilação e redeploy de todos, mesmo os que usavam uma fração da interface.
A ideia central: uma interface grande cria um acoplamento acidental. Se o cliente A depende de uma interface que também declara métodos usados só pelo cliente B, então A fica acoplado transitivamente às mudanças motivadas por B. Ao segregar a interface por papéis (role interfaces), cada cliente depende apenas do subconjunto que lhe interessa, e a superfície de acoplamento encolhe.
O problema que resolve
Seção intitulada “O problema que resolve”Interfaces gordas produzem três males:
- Implementações incompletas: uma classe obrigada a implementar métodos irrelevantes recorre a
throw new UnsupportedOperationException()ou corpos vazios — o que, de quebra, viola LSP - Liskov Substitution Principle. - Acoplamento e recompilação desnecessários: mudança num método usado por um cliente força os demais a recompilar/reimplementar.
- Baixa clareza de intenção: o contrato não comunica qual papel cada cliente exerce.
Como identificar violações
Seção intitulada “Como identificar violações”Trade-offs e quando não aplicar dogmaticamente
Seção intitulada “Trade-offs e quando não aplicar dogmaticamente”O excesso de segregação leva à explosão de interfaces minúsculas e ao aumento da complexidade de composição. Se toda operação vira uma interface de método único, você troca uma abstração grande difícil por dezenas de peças difíceis de orquestrar.
Não aplique ISP quando a interface já é coesa e todos os clientes usam todos os métodos, ou em contratos estáveis e pequenos onde a segregação só adicionaria cerimônia.
Exemplo em Java + Spring Boot 3
Seção intitulada “Exemplo em Java + Spring Boot 3”Antes — violando ISP
Seção intitulada “Antes — violando ISP”Uma interface Funcionario gorda força implementações a lidar com operações que não fazem sentido para elas.
public interface Funcionario { BigDecimal calcularSalario(); void registrarPonto(); void aprovarFerias(String matriculaSubordinado); void definirMetasEquipe(List<String> metas);}
public class Desenvolvedor implements Funcionario {
@Override public BigDecimal calcularSalario() { return new BigDecimal("12000"); }
@Override public void registrarPonto() { System.out.println("Ponto registrado"); }
@Override public void aprovarFerias(String matriculaSubordinado) { throw new UnsupportedOperationException("Desenvolvedor não aprova férias"); }
@Override public void definirMetasEquipe(List<String> metas) { throw new UnsupportedOperationException("Desenvolvedor não define metas de equipe"); }}Desenvolvedor é obrigado a implementar comportamentos de gestão que não possui, recorrendo a exceções — acoplamento acidental e violação de LSP embutida.
Depois — interfaces segregadas por papel
Seção intitulada “Depois — interfaces segregadas por papel”public interface Remunerado { BigDecimal calcularSalario();}
public interface Ponto { void registrarPonto();}
public interface Gestor { void aprovarFerias(String matriculaSubordinado); void definirMetasEquipe(List<String> metas);}@Componentpublic class Desenvolvedor implements Remunerado, Ponto {
@Override public BigDecimal calcularSalario() { return new BigDecimal("12000"); }
@Override public void registrarPonto() { System.out.println("Ponto registrado"); }}@Componentpublic class GerenteEngenharia implements Remunerado, Ponto, Gestor {
@Override public BigDecimal calcularSalario() { return new BigDecimal("22000"); }
@Override public void registrarPonto() { System.out.println("Ponto registrado"); }
@Override public void aprovarFerias(String matriculaSubordinado) { System.out.println("Férias aprovadas para " + matriculaSubordinado); }
@Override public void definirMetasEquipe(List<String> metas) { System.out.println("Metas definidas: " + metas); }}@Servicepublic class FolhaPagamento {
private final List<Remunerado> remunerados;
public FolhaPagamento(List<Remunerado> remunerados) { this.remunerados = remunerados; }
public BigDecimal totalFolha() { return remunerados.stream() .map(Remunerado::calcularSalario) .reduce(BigDecimal.ZERO, BigDecimal::add); }}FolhaPagamento depende apenas de Remunerado — não sabe nada sobre ponto ou gestão. Cada implementação assume só os papéis que de fato exerce, sem métodos-fantasma.
Diagrama
Seção intitulada “Diagrama”classDiagram
class Remunerado {
<<interface>>
+calcularSalario() BigDecimal
}
class Ponto {
<<interface>>
+registrarPonto()
}
class Gestor {
<<interface>>
+aprovarFerias(String)
+definirMetasEquipe(List)
}
class Desenvolvedor {
+calcularSalario() BigDecimal
+registrarPonto()
}
class GerenteEngenharia {
+calcularSalario() BigDecimal
+registrarPonto()
+aprovarFerias(String)
+definirMetasEquipe(List)
}
class FolhaPagamento {
+totalFolha() BigDecimal
}
Remunerado <|.. Desenvolvedor
Ponto <|.. Desenvolvedor
Remunerado <|.. GerenteEngenharia
Ponto <|.. GerenteEngenharia
Gestor <|.. GerenteEngenharia
FolhaPagamento --> Remunerado
Anti-padrões e erros comuns
Seção intitulada “Anti-padrões e erros comuns”Relações
Seção intitulada “Relações”- Faz parte de: SOLID
- É o SRP aplicado a contratos: SRP - Single Responsibility Principle.
- Habilita: DIP - Dependency Inversion Principle (abstrações enxutas e focadas são melhores alvos de dependência) e OCP - Open-Closed Principle.
- Evita violações de LSP - Liskov Substitution Principle (elimina métodos-fantasma que lançariam exceção).
- Base para Ports bem definidas na Arquitetura Hexagonal.
- Índice: Home
Perguntas de revisão
Seção intitulada “Perguntas de revisão”- Sob qual perspectiva o ISP deve ser avaliado: da implementação ou do cliente?
Resposta
Do cliente. A pergunta é se algum cliente é forçado a depender de mais do que usa, não simplesmente se a interface é “grande”.
- Por que uma interface gorda tende a induzir violações de LSP?
Resposta
Porque implementações que não têm sentido para certos métodos acabam usando UnsupportedOperationException ou corpos vazios, quebrando a substitutabilidade esperada do contrato.
- Qual é a granularidade recomendada para segregar interfaces?
Resposta
Por papel do cliente (role interface), não por método individual. Métodos sempre usados juntos pelo mesmo cliente devem permanecer na mesma interface.
- No exemplo, por que
FolhaPagamentonão deve depender deFuncionario?
Resposta
Porque FolhaPagamento só precisa de calcularSalario(). Depender da interface gorda Funcionario a acoplaria a mudanças em ponto e gestão, com as quais não tem relação. Depender de Remunerado minimiza a superfície de acoplamento.