Princípio de Segregação de Interface
Olá pessoal, vou fazer alguns posts sobre S.O.L.I.D. O primeiro que gostaria de abordar com vocês é:
- S
- O
- L
- IPrincípio da Segregação de Interface
- D
A definição basicamente é:
“Clients should not be forced to depend upon interfaces that they do not use.”
— Robert C. Martin e Micah Martin, Agile Principles, Patterns, and Practices in C#
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.
public interface IRepository<TEnt>
where TEnt : Entity {
TEnt Get(int id);
TEnt Save(TEnt entity);
void Delete(TEnt entity);
}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.
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.
public class OrderRepository : IOrderRepository {
public Order Get(int id) {
//Implementacao da busca da ordem no banco.
}
public Order Save(Order entity) {
//Implementacao da insercao/atualizacao da ordem no banco.
}
public void Delete(Order entity) {
//Este metodo eu nao vou utilizar porem ele esta aqui porque
//a interface IRepository obriga que eu o implemente.
throw new NotImplementedException();
}
}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.
public interface IDeleteRepository<in TEnt>
where TEnt : Entity {
void Delete(TEnt entity);
}
public interface IGetRepository<out TEnt>
where TEnt : Entity {
TEnt Get(int id);
}
public interface ISaveRepository<TEnt>
where TEnt : Entity {
TEnt Save(TEnt entity);
}
public interface IRepository<TEnt>
: ISaveRepository<TEnt>,
IDeleteRepository<TEnt>,
IGetRepository<TEnt>
where TEnt : Entity
{ }Notem agora que a interface IOrderRepository não estende mais a interface IRepository e sim as interfaces: IGetRepository e ISaveRepository.
public interface IOrderRepository
: IGetRepository<Order>, ISaveRepository<Order>
{ }Agora a nossa classe OrderRepository não contém mais o método Delete.
public class OrderRepository : IOrderRepository {
public Order Get(int id) {
//Implementacao da busca da ordem no banco.
}
public Order Save(Order entity) {
//Implementacao da insercao/atualizacao da ordem no banco.
}
}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.
public class Entity {
public int Id { get; protected set; }
}A classe abaixo é a minha classe de Order, note que ela herda da classe Entity que foi explicada acima.
public class Order : Entity { }Obrigado pela visita e espero que tenha gostado, qualquer dúvida é só entrar em contato.
Comentários