Princípios SOLID: Princípio da Segregação de Interface (ISP)

Continuando a série sobre os princípios SOLID, chegamos ao quarto princípio, conhecido por Princípio da Segregação de Interface (Interface Segregation Principle), ou simplesmente ISP.

Se você está chegando agora, lembro que os princípios SOLID são boas práticas de OO que ajudam a melhorar o design de nossas classes, removendo design smells e, por consequência, tornando o software mais fácil de ser mantido. Recomendo também a leitura dos posts anteriores sobre [SRP], [OCP] e [LSP].

DEFINIÇÃO

O Princípio da Segregação de Interface trata da coesão de interfaces e diz que clientes não devem ser forçados a depender de métodos que não usam.

EXEMPLO DE VIOLAÇÃO

Vejamos a seguinte interface:


public interface MembroDeTimeScrum
{
    void PriorizarBacklog();
    void BlindarTime();
    void ImplementarFuncionalidades();
}

E agora temos as classes Dev, ScrumMaster e ProductOwner implementando a interface MembroDeTimeScrum:


public class Dev : MembroDeTimeScrum
{
    public void PriorizarBacklog() { }
    public void BlindarTime() { }

    public void ImplementarFuncionalidades()
    {
        Console.Writeline("Codando e tomando café compulsivamente!!");
    }
}

public class ScrumMaster : MembroDeTimeScrum
{
    public void PriorizarBacklog() { }

    public void BlindarTime()
    {
        Console.Writeline("Devs working! You shall not pass!!!!");
    }

    public void ImplementarFuncionalidades() { }
}

public class ProductOwner : MembroDeTimeScrum
{
    public void PriorizarBacklog()
    {
        Console.Writeline("Priorizando backlog com base nas minhas necessidades de negócio");
    }

    public void BlindarTime() { }
    public void ImplementarFuncionalidades() { }
}

Ao criarmos uma interface genérica demais, acabamos fazendo com que uma implementação, no caso Dev, não utilize certos métodos da interface. É o que acontece com os métodos PriorizarBacklog e BlindarTime, que não fazem nada, pois não são atribuições de um Dev e sim do ProductOwner e do ScrumMaster, respectivamente.

PROBLEMAS

Suponhamos que alguma alteração seja necessária no método BlindarTime, que agora precisa receber alguns parâmetros. Dessa forma, somos obrigados a alterar todas implementações de MembroDeTimeScrum – Dev, ScrumMaster e ProductOwner – por causa de uma mudança que deveria afetar apenas a classe ScrumMaster.

Além disso, classes-cliente que dependiam de MembroDeTimeScrum terão que ser recompiladas e se estão em diversos componentes terão que ser redistribuídas. Algumas vezes desnecessariamente, pois nem sequer faziam uso do método BlindarTime!

Outro problema é que a implementação de métodos inúteis (chamados “degenerados”) pode levar à violação do LSP, pois alguém utilizando MembroDeTimeScrum poderia supor o seguinte:

foreach(var membro in membrosDeTimeScrum)
    membro.ImplementarFuncionalidades();

No entanto, sabemos que apenas Dev executa o comportamento acima. Se a lista tivesse também objetos do tipo ScrumMaster ou ProductOwner, esses objetos não estariam realizando nada, ou pior, poderiam disparar alguma exceção, caso a implementação dos mesmos assim o fizesse.

RESOLVENDO A VIOLAÇÃO DO ISP

A solução para o exemplo acima seria criamos interfaces mais específicas para que cada classe cliente dependa apenas do que realmente necessita. Por exemplo:


public interface FuncaoDeScrumMaster
{
    void BlindarTime();
}

public class ScrumMaster : FuncaoDeScrumMaster
{
    public void BlindarTime()
    {
        Console.Writeline("Devs working! You shall not pass!!!!");
    }
}

Com a alteração acima, a classe concreta ScrumMaster não precisa mais implementar métodos desnecessários e demais classes que dependiam de MembroDeTimeScrum apenas para utilizar BlindarTime podem agora depender da interface FuncaoDeScrumMaster.

A mesma ideia pode ser aplicada para as funções específicas de Dev e ProductOwner. Assim todos os clientes de MembroDeTimeScrum agora podem depender especificamente das interfaces que utilizam.

CONCLUSÃO

O Princípio da Segregação de Interface nos alerta quanto à dependência em relação a “interfaces gordas”, forçando que classes concretas implementem métodos desnecessários e causando um acoplamento grande entre todos os clientes.

Ao usarmos interfaces mais específicas, quebramos esse acoplamento entre as classes clientes, além de deixarmos as implementações mais limpas e coesas.
—————————–
Toda a série:
Princípio da Responsabilidade Única (SRP)
Princípio do Aberto/Fechado (OCP)
Princípio da Substituição de Liskov (LSP)
Princípio da Segregação de Interface (ISP)
Princípio da Inversão de Dependência (DIP)

19 comentários em “Princípios SOLID: Princípio da Segregação de Interface (ISP)

Adicione o seu

  1. “You shall not pass!!!!” – (risos), muito bom! O Gandalf daria um bom Scrum Master mesmo.
    Bem interessante. Sempre achei que o ISP tinha a ver com separar/segregar ações/métodos em interfaces mais granulares, porém nunca pensei desse ponto de vista de ‘clientes’ sendo obrigados a implementar métodos desnecessários.
    Sendo eu um desenvolvedor novo só usei interfaces quando fiz um TDD ou coisa do tipo, ai só utilizo interfaces quando precisa mesmo, e geralmente a interface num tem mais do que uma implementação para ela. Como era no caso dos Repositórios ai…
    Se bem que… “Interface” pode não representar necessariamente “Interface recurso de programação”, poderia também representar os métodos públicos ou protegidos de uma classe base, ou to enganado?

    Curtir

    1. Pois é
      A ideia é que qto mais “coisas” desnecessárias os clientes conhecerem é maior a chance de um grande impacto em futuras modificações.

      Qto aos repositórios, por mais que exista (normalmente) uma única implementação, é justificável seu uso para inverter a dependência entre domínio e infra. (Provavelmente este será o exemplo que darei no próximo post, sobre DIP.). Outro ponto positivo é a testabilidade.

      E sim, “interface” pode ser usado neste contexto de “o quê sua classe expõe para o mundo”. No entanto o princípio se refere mesmo à interface (abstração), o “plug” que conecta cliente e implementação.

      []s, Teteus!

      Curtir

  2. Robson,
    Parabéns pelo post, muito boa didática e o exemplo abordado.
    Quando trabalhamos com injeção de dependência, pela necessidade de termos uma interface para cada classe concreta, acabamos atendendo automaticamente este princípio SOLID correto?

    Curtir

    1. Olá, Danilo
      Obrigado!

      Na verdade, não! Você pode injetar uma “interface gorda” como a do primeiro código, ou seja, o principio continua quebrado. Então DI não implica em atender este princípio.
      Qualquer coisa, estamos aí.
      []s

      Curtir

  3. Olha, parabéns pelos artigos.

    Sempre procuro conteúdo na gringa pelo preconceito de que não existe conteúdo bom local.

    Pesquisei inúmeros artigos sobre solid(inclusive as gringas) e o seu com certeza foi o mais bem explicado que achei.

    Curtir

  4. Pingback: Jessyka Lage

Deixar mensagem para Danilo Miranda Cancelar resposta

Blog no WordPress.com.

Acima ↑