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)