C# - Violações dos princípios SOLID - IV
Hoje vamos continuar mostrando as violações dos principíos SOLID que fazem parte do dia a dia de todo o desenvolvedor. |
Continuando o artigo anterior veremos agora a violação do princípio SOLID SRP.
O princípio SOLID SRP (Single Responsibility Principle, ou Princípio da Responsabilidade Única, em português) é um dos cinco princípios SOLID da programação orientada a objetos. Ele estabelece que uma classe deve ter uma única responsabilidade bem definida e ser modificada apenas por uma única razão.
O princípio SRP prega que cada classe deve ter uma única tarefa ou responsabilidade no sistema e que essa responsabilidade deve estar completamente encapsulada dentro da própria classe. Isso significa que uma classe não deve fazer mais do que uma coisa, e não deve ter conhecimento ou depender de outras classes para realizar sua responsabilidade.
Ao seguir o princípio SRP, o código fica mais fácil de entender, manter e evoluir no futuro, pois as classes são mais coesas e independentes umas das outras. Além disso, as mudanças em uma parte do sistema não afetam outras partes, tornando o código mais modular e escalável.
Violação do princípio SRP
Uma violação comum do princípio SRP é a presença de uma classe que tem muitas responsabilidades diferentes, ou seja, que faz muitas coisas diferentes dentro do sistema. Isso pode acontecer quando uma classe é modificada várias vezes para atender a diferentes requisitos ou necessidades do sistema, sem levar em consideração a coesão das responsabilidades.
Por exemplo, imagine uma classe chamada "Funcionario" que é responsável por lidar com todas as informações do funcionário, como registro de horas trabalhadas, folha de pagamento, cálculo de impostos, gerenciamento de benefícios, entre outros. Essa classe estaria violando o princípio SRP, pois tem muitas responsabilidades diferentes.
Para resolver essa violação do SRP, seria necessário separar as responsabilidades da classe "Funcionario" em classes distintas e coesas, como "RegistroDeHoras", "FolhaDePagamento", "CalculoDeImpostos", "GerenciamentoDeBeneficios", entre outras. Dessa forma, cada classe teria uma única responsabilidade bem definida, tornando o código mais fácil de entender, manter e evoluir no futuro.
Veja um exemplo de código que ilustra essa violação usando o C# 11 no VS 2022 com o recurso do Top Level Statement:
public
class
Funcionario { public int Id { get; set; } public string Nome { get; set; } public string Email { get; set; } public decimal Salario { get; set; } public void RegistrarHorasTrabalhadas(DateTime data, int horasTrabalhadas) { // Lógica para registrar horas trabalhadas } public void CalcularSalario() { // Lógica para calcular o salário } public decimal CalcularImpostos() { // Lógica para calcular Impostos } public void GerenciarBeneficios() { // Lógica para gerenciar benefícios } } |
Nesse exemplo, a classe "Funcionario" tem as propriedades para armazenar informações do funcionário e quatro métodos, um para registrar as horas trabalhadas, outro para calcular o salário, outro para calcular os impostos e outro para gerenciar os benefícios do funcionário. Essa classe tem mais de uma responsabilidade, o que torna mais difícil entender, testar e manter o código no futuro.
Para resolver essa violação do princípio SRP, poderíamos dividir as responsabilidades em outras classes distintas e coesas. Por exemplo, ter uma classe para armazenar informações do funcionário, outra para calcular o salário, outra para calcular impostos e outra para gerenciar os benefícios. Dessa forma, cada classe teria uma única responsabilidade bem definida, o que tornaria o código mais modular e fácil de entender, testar e manter no futuro.
A seguir temos um esboço de código que mostra uma implementação possível para resolver este problema:
public
class
Funcionario { public int Id { get; set; } public string Nome { get; set; } public string Email { get; set; } public decimal Salario { get; set; } } public class RegistroDeHorasTrabalhadas{ public void RegistrarHorasTrabalhadas(Funcionario funcionario, DateTime data, int horasTrabalhadas) { // Lógica para registrar horas trabalhadas } } public class CalculadoraDeSalario{ public decimal CalcularSalario(Funcionario funcionario) { // Lógica para calcular o salário } } public class CalculadoraDeImpostos{ public decimal CalcularImpostos(Funcionario funcionario) { // Lógica para calcular impostos } } public class GerenciadorDeBeneficios{ public void GerenciarBeneficios(Funcionario funcionario) { // Lógica para gerenciar benefícios } } |
Nesse exemplo, a classe "Funcionario" foi dividida em quatro outras classes: "Funcionario", "RegistroDeHorasTrabalhadas", "CalculadoraDeSalario", "CalculadoraDeImpostos" e "GerenciadorDeBeneficios".
Agora, cada classe tem uma única responsabilidade e está aderente ao princípio SRP.
Ao utilizar essa abordagem, torna-se mais fácil entender, testar e manter o código no futuro, além de permitir que cada classe possa ser alterada independentemente das outras.
Padrões que podem violar o princípio
Um
padrão que pode violar o princípio SRP é o padrão "Facade".
Esse padrão é usado para fornecer uma interface simplificada
para um sistema complexo, encapsulando a lógica do sistema em
uma única classe ou conjunto de classes. No entanto, se a classe
"Facade" se tornar muito grande e tiver várias
responsabilidades, ela pode violar o princípio SRP.
Outro padrão que pode violar o princípio SRP é o padrão "Adapter".
Este padrão é usado para adaptar uma interface de classe para
outra interface compatível. Se a classe de adaptação também
tiver outras responsabilidades além de adaptar a interface, ela
poderá violar o princípio SRP.
Exemplo de violação do princípio pelo padrão Adapter
Imagine que você está desenvolvendo uma aplicação de e-commerce que precisa integrar com um serviço de pagamento de terceiros chamado "PaymentGateway". O serviço de pagamento possui uma interface que espera um objeto com as informações do pagamento para que possa processá-lo. No entanto, a aplicação de e-commerce já possui uma classe chamada "Order" que contém todas as informações necessárias para realizar um pagamento, mas não está em conformidade com a interface esperada pelo serviço de pagamento.
Para resolver esse problema, você decide implementar um adaptador que converte a classe "Order" na interface esperada pelo serviço de pagamento. O adaptador é implementado na classe "PaymentGatewayAdapter", que contém um método "ProcessPayment" que converte uma instância da classe "Order" em um objeto compatível com o serviço de pagamento e o envia para o serviço.
No entanto, você também decidiu que a classe "PaymentGatewayAdapter" seria responsável por gerenciar o cache de pedidos processados para evitar pedidos duplicados. Portanto, além de adaptar a classe "Order" para a interface do serviço de pagamento, a classe "PaymentGatewayAdapter" também tem a responsabilidade de gerenciar o cache de pedidos.
Este é um exemplo de violação do princípio SRP pelo padrão Adapter, porque a classe "PaymentGatewayAdapter" tem mais de uma responsabilidade - adaptar a classe "Order" para a interface do serviço de pagamento e gerenciar o cache de pedidos - o que pode tornar a classe difícil de manter e modificar.
Exemplo de código que ilustra a violação:
public class
Order { public int Id { get; set; } public string CustomerName { get; set; } public decimal Total { get; set; } // outras propriedades do pedido }
public interface
IPaymentGateway
public class PaymentGatewayAdapter :
IPaymentGateway
public PaymentGatewayAdapter(IPaymentGateway paymentGateway)
public void ProcessPayment(object
paymentData) paymentGateway.ProcessPayment(paymentInfo);
// adiciona o pedido ao cache de pedidos processados
// método que viola o princípio SRP |
Neste exemplo, a classe "PaymentGatewayAdapter" é responsável por adaptar a classe "Order" para a interface "IPaymentGateway" e gerenciar o cache de pedidos processados.
No entanto, a responsabilidade de gerenciar o cache de pedidos é uma responsabilidade separada e deve ser delegada a outra classe para evitar violação do princípio SRP.
E na próxima parte do artigo veremos a violação do princípio DIP.
E estamos conversados ...
"Há um caminho que ao homem parece direito, mas o fim dele são os caminhos da
morte."
Provérbios 14:12
Referências:
LINQ - Gerando um Produto Cartesiano - Macoratti
C# - Salvando e Lendo informações em um arquivo XML - Macoratti