Chegamos a quarta parte da série sobre os padrões SOLID apresentados aqui no NET Coders. Hoje o assunto abordado será o ISP (Interface Segregation Principle) ou Princípio da Segregação de Interface.

Uma pitada de Programação Orientada a Objetos

Antes de entender o que é o ISP, vamos falar um pouco sobre POO (Programação Orientada a Objeto) e Interfaces. Um dos grandes paradigmas da POO diz que é muito melhor programar para uma abstração do que para uma implementação. O uso das interfaces torna o código menos acoplado, extensível e assim gerando menos problemas em manutenções ou adições de novas funcionalidades. Embora com “popularização” da POO, muitos desenvolvedores de diversos níveis de experiência, principalmente a galera que vem do Delphi e/ou do VB6, ainda tem problemas para usar interfaces e programar em alto nível de abstração. Acabam deixando um dos maiores recursos da POO de lado ou fazendo uso inadequado.

Voltando ao ISP…

Este princípio é bem simples de entender e sua aplicação é justamente em oposição a interfaces genéricas demais, onde dependendo do cenário, pode acabar ocorrendo o problema de uma classe ter que implementar métodos que não usará.

O ISP possui um mantra que diz:

Muitas interfaces específicas são melhores do que uma interface única.

Outra frase bastante usada para definir o ISP é:

Clientes não devem ser forçados a depender de métodos que não usam

Podemos traduzir “Clientes” para uma classe, que pode ser um serviço ou um repositório, por exemplo.

É bem comum este princípio ser aplicado para resolver problemas de violação do LSP (veja aqui o artigo sobre o LSP), em alguns casos, um acaba complementando o outro.

O ISP ajuda quando uma determinada interface se torna “gorda” ou genérica demais com muitas responsabilidades e aplicando esse padrão, podemos subdividir essa interface em partes menores com responsabilidades mais específicas (assim fazendo valer seu mantra).

Vou apresentar um cenário bem comum do nosso dia-a-dia. E como nos artigos anteriores, antes vou mostrar a violação e depois a solução.

Cenário:

Teremos um repositório genérico com operações de escrita e leitura, porém vamos precisar segregar essas atividades, pois existem objetos que não podem ter métodos de escrita e sim apenas métodos de leitura, aplicando o ISP, vamos resolver esse problema.

Na Listagem 01 eis o nosso repositório genérico com as operações de CRUD (Create, Read, Update e Delete).

public interface IBaseRepository<TEntity> where TEntity : class
{
   void Gravar(TEntity obj);
   void Atualizar(TEntity obj);
   IEnumerable<TEntity> ObterTodos();
   TEntity ObterPorId(int id);
}

Listagem 01 – IBaseRepository

Até aqui tudo normal, uma interface usada para aplicar o Repository Pattern para operações de escrita e leitura.

Em nosso contexto temos a classe Cliente que terá a sua própria implementação de IBaseRepository apresentado na Listagem 02.

public class ClienteRepository: IBaseRepository<Cliente>
{
     public void Gravar(Cliente obj)
     {
         //ação de gravar
     }

     public void Atualizar(Cliente obj)
     {
        //ação de atualizar
     }

     public IEnumerable<Cliente> ObterTodos()
     {
       //ação de listar todos
     }

     public Cliente ObterPorId(int id)
     {
       //ação de listar por id
     }
}

Listagem 02 – Repositório de Cliente implementado.

Violando o ISP

Em todas as classes que é necessário ter as operações de CRUD eu posso implementar essa interface sem problema algum, mas e se precisar implementar o repositório em uma entidade que tenha somente operações de leitura? Por exemplo uma entidade Cidade.

Na Listagem 03 veremos que se implementarmos o IBaseRepository para Cidade vamos violar o ISP já que os métodos Gravar e Atualizar simplesmente não fazem parte do contexto da entidade Cidade.

public class CidadeRepository :IBaseRepository<Cidade>
{
    public void Gravar(Cidade obj)
    {
       throw new Exception("Não existe operação de gravação para Cidade");
    }

    public void Atualizar(Cidade obj)
    {
       throw new Exception("Não existe operação de atualização para Cidade");
    }

    public IEnumerable<Cidade> ObterTodos()
    {
       //ação para obter todas as cidades
    }

    public Cidade ObterPorId(int id)
    {
      //ação para obter uma cidade por id
    }
}

Listagem 03 – Violando o ISP.

 Arrumando a casa!

Como temos a necessidade de implementar repositórios em entidades que podem ou não possuir operações de escrita, fazendo uso do ISP vamos dividir uma única interface genérica em duas interfaces com responsabilidades específicas.

A interface IBaseRepository dará lugar a duas novas interfaces:

IBaseLeituraRepository – para leitura dos dados.

IBaseEscritaRepository – Para escrita dos dados.

public interface IBaseEscritaRepository<TEntity> where TEntity : class
{
   void Gravar(TEntity obj);
   void Atualizar(TEntity obj);
}

public interface IBaseLeituraRepository<TEntity> where TEntity : class
{
   IEnumerable<TEntity> ObterTodos();
   TEntity ObterPorId(int id);
}

Listagem 04 – ISP aplicado a interface IBaseRepository

A classe ClienteRepository, agora em vez de ter uma única interface com operações de leitura e escrita, terá uma interface para ler os dados e outra interface para escrever os dados.

 public class ClienteRepository: IBaseEscritaRepository<Cliente>, IBaseLeituraRepository<Cliente>
 {
     public void Gravar(Cliente obj)
     {
        //ação de gravar
     }

     public void Atualizar(Cliente obj)
     {
        //ação de atualizar
     }

     public IEnumerable<Cliente> ObterTodos()
     {
        //ação listar todos
     }

     public Cliente ObterPorId(int id)
     {
        //ação de listar por id
     }
 }

Listagem 05 – Mudança da implementação de ClienteRepository.

A classe CidadeRepository que violou o ISP anteriormente, agora com essa nova interface de leitura não precisará implementar métodos que não usa.

public class CidadeRepository:IBaseLeituraRepository<Cidade>
{
    public IEnumerable<Cidade> ObterTodos()
    {
      return null;
    }

    public Cidade ObterPorId(int id)
    {
      return null;
    }
}

Listagem 06 – Violação do ISP resolvida em CidadeRepository.

 

Bom, no último artigo da série sobre SOLID, o assunto abordado será o DIP (Dependency Inversion Principle), que por sua vez é aplicado em conjunto com o ISP. Ao longo de cada artigo, estamos vendo que cada ideia defendida pelo SOLID nada mais é que um complemento da outra e juntas formam uma maneira coesa de desenvolver Orientado a Objeto. Até lá!

 

Referências:

An introduction to Interface Segregation Principle (ISP)

Dependency inversion principle

 

 

Diego Neves

Desenvolvedor de software, graduado em Análise e Desenvolvimento de Sistemas pela Universidade Nove de Julho, atualmente trabalha na FCamara e tem foco no mundo .NET, possui algumas certificações como MCSD Web Applications, MCSD App Builder, MCSA Web Applications, Microsoft Specialist, MCP e ITIL V3 Foundation.

Facebook LinkedIn 

Comentários

comentarios