Júnior Oliveira

C Sharp; Delphi; ASP .NET; PHP; Javascript;

Princípio de Segregação de Interface

02 Sep 2012 csharp, solid

Olá pessoal, vou fazer alguns posts sobre S.O.L.I.D. O primeiro que gostaria de abordar com vocês é:

  S
  O
  L
  ISP - Interface Segregation Principle
  D

A definição basicamente é:

"Clients should not be forced to depend upon interfaces that they do not use."

Para mais detalhes sobre a definição, segue o link do artigo The Interface Segregation Principle que é um resumo de um capitulo do livro "Agile Principles, Patterns, and Practices in C#" de Martin C. Robert e Martin Micah.

Gostaria de mostrar um exemplo real onde o principio é quebrado. O exemplo está em C#.

O meu problema era que eu tinha uma interface onde todas as classes que precisavam fazer alguma persistência tinham que implementar, nesta interface eu tinha definido três métodos: Save, Delete e GetBy, porém, nem todas as classes precisavam implementar todos estes métodos.

1 public interface IRepository<TEnt>
2     where TEnt : Entity {
3 
4     TEnt Get(int id);
5     TEnt Save(TEnt entity);
6     void Delete(TEnt entity);
7 }

O próximo código mostra uma interface para definir as operações que uma ordem poderá realizar na base de dados, já que a mesma estende da interface IRepository.

1 public interface IOrderRepository : IRepository<Order> { }

O código abaixo é a classe que irá implementar os métodos definidos na interface IOrderRepository que por sua vez irá implementar os métodos definidos na interface IRepository já que a interface IOrderRepository estende da interface IRepository.

 1 public class OrderRepository : IOrderRepository {
 2 
 3     public Order Get(int id) {
 4         //Implementacao da busca da ordem no banco.
 5     }
 6 
 7     public Order Save(Order entity) {
 8         //Implementacao da insercao/atualizacao da ordem no banco.
 9     }
10 
11     public void Delete(Order entity) {
12         //Este metodo eu nao vou utilizar porem ele esta aqui porque
13         //a interface IRepository obriga que eu o implemente.
14         throw new NotImplementedException();
15     }
16 }

Percebem que o método Delete tem um throw new NotImplementedException(), porque eu não vou usar este método para nada, ele só está ai porque está definido na minha interface IRepository, e por este motivo eu sou obrigado a implementá-lo, o problema é que quando eu for utilizar a classe OrderRepository não tem como eu saber se o método delete tem implementação ou não e quando eu chamar o mesmo irá gerar uma exceção. Este é um exemplo que quebra o principio ISP.

Abaixo uma forma de resolver este problema. Notem que eu quebrei a interface IRepository em outras três interfaces:

  • IDeleteRepository: Que irá conter a definição do método Delete.
  • IGetRepository: Que irá conter a definição do método GetBy.
  • ISaveRepository: Que irá conter a definição do método Save.

A interface IRepository agora estende das três interfaces definidas acima, caso tenha alguma interface que precise definir as três operações, pode estender da interface IRepository.

 1 public interface IDeleteRepository<in TEnt>
 2     where TEnt : Entity {
 3 
 4     void Delete(TEnt entity);
 5 }
 6 
 7 public interface IGetRepository<out TEnt>
 8     where TEnt : Entity {
 9 
10     TEnt Get(int id);
11 }
12 
13 public interface ISaveRepository<TEnt>
14     where TEnt : Entity {
15 
16     TEnt Save(TEnt entity);
17 }
18 
19 public interface IRepository<TEnt>
20     : ISaveRepository<TEnt>,
21         IDeleteRepository<TEnt>,
22         IGetRepository<TEnt>
23     where TEnt : Entity 
24 { }

Notem agora que a interface IOrderRepository não estende mais a interface IRepository e sim as interfaces: IGetRepository e ISaveRepository.

1 public interface IOrderRepository
2     : IGetRepository<Order>, ISaveRepository<Order> 
3 { }

Agora a nossa classe OrderRepository não contém mais o método Delete.

 1 public class OrderRepository : IOrderRepository {
 2 
 3     public Order Get(int id) {
 4         //Implementacao da busca da ordem no banco.
 5     }
 6 
 7     public Order Save(Order entity) {
 8         //Implementacao da insercao/atualizacao da ordem no banco.
 9     }
10 }

Vocês podem notar que para resolver o problema do throw new NotImplementedException() eu tive que criar interfaces mais especificas, e a nossa classe final contém agora apenas os métodos que realmente precisa.

O código abaixo não é importante para o nosso exemplo, será mostrando apenas para completar os códigos acima.

Está é a classe base das minhas entidades. Como podem notar a interface IRepository pede a definição de um tipo que herde está classe. Note que ela contém uma propriedade chamada Id do tipo int, caso você tenha a necessidade de ter em cada entidade uma propriedade Id de um tipo diferente você precisa apenas deixar genérico o tipo desta propriedade, como é apenas um exemplo não adicionei está complexidade a minha classe.

1 public class Entity {
2 
3     public int Id { get; protected set; }
4 }

A classe abaixo é a minha classe de Order, note que ela herda da classe Entity que foi explicada acima.

1 public class Order : Entity { }

Obrigado pela visita e espero que tenha gostado, qualquer dúvida é só entrar em contato.