SOLID (1/5)
Um dos conceitos mais solicitados hoje no mundo do desenvolvimento é o conhecimento de SOLID. Nesse post, vamos explorar o SRP - Single Responsibility Principle.
SOLID é um acrônimo com as iniciais de um conjunto de 5 princípios introduzidos por Robert C. Martin com o paper “Design Principles and Design Patterns” em 2000 e consolidado por Michael Feathers em 2004.
- SRP - Single Responsibility Principle (Princípio da responsabilidade única)
- OCP - Open-Closed Principle (Princípio aberto-fechado)
- LSP - Liskov Substitution Principle (Princípio de substituição de Liskov)
- ISP - Interface Segregation Principle (Princípio de segregação de interface)
- DIP - Dependency Inversion Principle (Princípio de inversão de dependência)
Vamos detalhar todos os princípios, em uma série de 5 posts, o primeiro princípio que vamos abordar é o Princípio da Responsabilidade Única.
SRP - Single Responsibility Principle
O nome deste princípio é direto, o que torna o entendimento dele aparentemente simples. Por definição, o SRP diz que cada módulo do software deve ter uma e apenas uma razão de mudança. Mas como definir uma razão de mudança? Quando um módulo se torna acoplado demais? Vamos analisar os exemplos abaixo e entender melhor esse princípio.
public class Pedido {
...
public void CriarPedido(){
...
}
public void AdicionarItemPedido(){
...
}
public void Pagamento(){
...
}
public void IniciarTransporte(){
...
}
public void RastrearTransporte(){
...
}
}
No exemplo acima, a classe Pedido
tem responsabilidades demais. Além de gerenciar o pedido, ela está gerenciando pagamento e transporte. Imagine que, se houver uma mudança no rastreamento do transporte, nossa classe Pedido
terá que ser alterada, mesmo que o rastreamento do transporte não seja relevante para o conceito do pedido. Nesse caso, precisamos refatorar esse código e criar outras classes para dividirem as responsabilidades.
public class Pedido {
public int Id { get; set; }
public List<ItemPedido> Itens { get; set; }
public DateTime DataCriacao { get; set; }
public void AdicionarItem(ItemPedido item){
...
}
public void RemoverItem(int itemId){
...
}
public decimal CalcularTotal(){
...
}
}
public class ProcessadorPagamento {
public bool ProcessarPagamento(Pedido pedido, FormaPagamento forma){
...
}
public bool ValidarPagamento(decimal valor){
...
}
}
public class GerenciadorTransporte {
public void IniciarTransporte(Pedido pedido, Endereco destino){
...
}
public StatusTransporte RastrearTransporte(string codigoRastreamento){
...
}
}
Agora, com as classes refatoradas, temos 3 classes, cada uma com uma responsabilidade bem definida:
- Pedido: Responsável apenas por gerenciar os dados e operações básicas do pedido (adicionar/remover itens, calcular total)
- ProcessadorPagamento: Responsável exclusivamente pelo processamento e validação de pagamentos
- GerenciadorTransporte: Responsável apenas pelas operações de transporte e rastreamento
Cada classe agora tem uma única razão para mudar: alterações no conceito específico que ela representa. Por exemplo, se mudarmos a forma de calcular o valor do pedido, apenas a classe Pedido
será afetada. Se alterarmos a API de pagamento, apenas ProcessadorPagamento
precisará ser modificada.
Vantagens da aplicação do SRP:
- Manutenibilidade: Cada classe é mais fácil de entender e manter
- Testabilidade: Podemos testar cada responsabilidade isoladamente
- Reutilização: As classes podem ser reutilizadas em diferentes contextos
- Baixo acoplamento: Mudanças em uma classe não afetam as outras