“Interface Segregation Principle” diz o seguinte:
“Os clientes não devem ser forçados a depender de interfaces que não utilizam.”
É o “SRP” aplicado em interfaces.
Neste artigo explico o princípio com dois exemplos práticos.
Em um exemplo iremos segregar a interface para facilitar o uso e a criação de novas classes.
Em um outro exemplo iremos segregar a interface para restringir o acesso ao objeto.
Exemplo de uma interface que se torna inchada#
Veja o seguinte código:
// Interface com as capacidades de um smartphone
interface SmartPhone {
void tocaMusica();
void tiraFoto();
}
class IPhone13 implements SmartPhone {
@Override
public void tocaMusica() {
System.out.println("Tocando musica no iPhone 13");
}
@Override
public void tiraFoto() {
System.out.println("Tirando foto no iPhone 13");
}
}
// Problema!
class CelularAntigo implements SmartPhone {
@Override
public void tocaMusica() {
System.out.println("Tocando musica no celular antigo");
}
// Por usar a interface, a classe é obrigado a implementar
// Mas é ruim pois causa efeitos colaterais:
// - Ou criamos um método que lança uma excecao
// - Ou criamos um método vazio
// Ou seja, é um desperdicio e pode confundir o usuario deste objeto.
@Override
public void tiraFoto() {
throw new UnsupportedOperationException("Celular antigo nao tira foto");
}
}
Este exemplo ilustra como uma interface pode ficar sobrecarregada de responsabilidades. Um sintoma comum é a presença de métodos que não deveriam existir, já que o próprio conceito da classe não abrange todos os comportamentos esperados.
Uma refatoração simples pode melhorar a situação:
// Criamos uma interface especifica para cada capacidade
interface TemPlayer {
void tocaMusica();
}
interface TemCamera {
void tiraFoto();
}
// Mantemos a interface original
// para diminuir o custo da alteracao
interface SmartPhone extends TemPlayer, TemCamera {}
class IPhone13 implements SmartPhone {
@Override
public void tocaMusica() {
System.out.println("Tocando musica no iPhone 13");
}
@Override
public void tiraFoto() {
System.out.println("Tirando foto no iPhone 13");
}
}
// Celular antigo agora so implementa a interface
// ‘TemPlayer'
class CelularAntigo implements TemPlayer {
@Override
public void tocaMusica() {
System.out.println("Tocando musica no celular antigo");
}
}
Ao reorganizar o exemplo anterior seguindo o “ISP” , garantimos interfaces coesas e classes focadas em comportamentos específicos.
Mas muitas vezes nos encontramos em situações onde a interface já está inchada:
public class DispositivoInteligenteExample {
public interface DispositivoInteligente {
void ligar();
void desligar();
void reproduzirMusica();
void ajustarVolume();
void exibirNotificacoes();
void controlarIluminacao();
}
public static class AltoFalanteInteligente implements DispositivoInteligente {
public void ligar() {}
Nestas situações, para não ter o custo alto de alteração, recomendo criar interfaces segregadas aos poucos para aplicar o ISP e o SRP na classe e na interface:
public class DispositivoInteligenteRefactoredExample {
public interface DispositivoBasico {
void ligar();
void desligar();
}
public interface DispositivoInteligente extends DispositivoBasico {
void reproduzirMusica();
void ajustarVolume();
void exibirNotificacoes();
void controlarIluminacao();
}
public static class AltoFalanteInteligente implements DispositivoInteligente {
public void ligar() {}
public void desligar() {}
Exemplo de segregação de API de um objeto#
Quando criamos um design interessante da nossa regra de negócio, pode acontecer de um objeto ser acessado por dois ou mais tipos de usuários finais.
Por exemplo, vamos imaginar um sistema escolar onde o aluno pode consultar as notas das provas e os professores podem adicionar notas da sua disciplina ao aluno:
public class AlunoExample {
public static class Aluno {
private List<Nota> notas;
public String imprimeNotas() {
return "";
}
public void adicionaNota(Nota nota) {}
}
interface AlunoService {
Aluno buscaPorNome(String nome);
}
A modelagem parece legal mas pode criar um problema. Se um aluno conseguir acessar a API do objeto aluno, ele pode adicionar notas pois o objeto permite!
“ISP” pode ajudar nesta situação também:
class Nota {
// Placeholder for Nota class
}
/*
* Interface especifica para consulta de notas
*/
interface AlunoComNotas {
String imprimeNotas();
}
/*
* Interface especifica para adicionar notas
*/
interface AlunoParaDarNota {
void adicionaNota(Nota nota);
}
// Removo o 'public' da classe para ninguém ter acesso
class Aluno implements AlunoComNotas, AlunoParaDarNota {
private List<Nota> notas;
// Alunos e professores podem acessar
public String imprimeNotas() {
return "";
}
// Somente professor pode acessar
public void adicionaNota(Nota nota) {
/* ... */
}
}
interface AlunoService {
AlunoComNotas buscaPorNome(String nome);
A ideia deste exemplo é segregar a API do objeto aluno criando duas interfaces públicas.
- Uma interface AlunoComNotas onde alunos e professores podem acessar.
- Uma interface AlunoParaDarNota onde somente os professores podem acessar.
E deixamos a implementação escondida do mundo externo, removendo o public da classe.
Com esta segregação conseguimos refletir a lógica de autorização de acesso com interfaces específicas em objetos concretos.
Conclusão#
“ISP” é um princípio que ajuda a refletir melhor a sua interpretação sobre a abstração do mundo da sua regra de negócio.
Uma dica importante sobre como aplicar o “ISP” é sempre usar conceitos que o pessoal de negócio, usuário final ou cliente utilizam.
Os conceitos, verbos e ações utilizados por estas pessoas tendem a ser coesas pela natureza da regra de negócio. Se não for coesa, fica difícil de entender.
E não fiquem viciados em criar interfaces para todas as classes de serviços! Criem e apliquem o “ISP” com propósito!
E pelo amor de deus, nada de “IPessoaService”, “IAlunoService” e etc… Usar o nome para deixar claro que o que você está usando a interface é pouco eficiente.
Lembre-se do polimorfismo: Não importa o objeto que estou utilizando, contanto que ele faça muito bem o trabalho que estou pedindo.
🥒🥒 Espero que tenham curtido e até o próximo artigo! 🥒🥒


