Introdução aos princípios SOLID em C#
\nDesenvolver em C# no ecossistema .NET exige não apenas conhecimento de sintaxe, mas também habilidade para projetar sistemas flexíveis, de fácil manutenção e testáveis. Foi exatamente para isso que foram criados os princípios SOLID — cinco regras fundamentais da programação orientada a objetos, formuladas por Robert Martin (Uncle Bob).
\nO acrônimo SOLID significa:
\n- \n
- S — Single Responsibility Principle (Princípio da Responsabilidade Única) \n
- O — Open/Closed Principle (Princípio Aberto/Fechado) \n
- L — Liskov Substitution Principle (Princípio da Substituição de Liskov) \n
- I — Interface Segregation Principle (Princípio da Segregação de Interfaces) \n
- D — Dependency Inversion Principle (Princípio da Inversão de Dependências) \n
Neste artigo, analisaremos cada princípio na prática com exemplos em C#, mostraremos como sua violação leva a problemas e como a aplicação correta torna o código mais limpo.
\n\n1. Princípio da Responsabilidade Única (SRP)
\nEssência: Uma classe deve ter apenas um motivo para ser alterada. Em outras palavras, cada classe é responsável por uma tarefa claramente definida.
\n\nViolando o SRP em C#
\nVamos considerar um exemplo típico — a classe OrderService, que faz coisas demais:
public class OrderService\n{\n public void ProcessOrder(Order order)\n {\n // 1. Salvar no banco de dados\n SaveToDatabase(order);\n \n // 2. Enviar email\n SendEmail(order.CustomerEmail, "Pedido confirmado");\n \n // 3. Registrar log\n File.WriteAllText("log.txt", $"Pedido {order.Id} processado");\n }\n \n private void SaveToDatabase(Order order) { /* ... */ }\n private void SendEmail(string email, string message) { /* ... */ }\n}\nProblema: Alterar a lógica de envio de email ou a forma de registrar logs resultará na modificação da classe OrderService. Isso viola o SRP.
Implementação correta do SRP
\nSeparamos as responsabilidades em serviços distintos:
\npublic class OrderProcessor\n{\n private readonly IOrderRepository _repository;\n private readonly INotificationService _notificationService;\n private readonly ILogger _logger;\n \n public OrderProcessor(\n IOrderRepository repository,\n INotificationService notificationService,\n ILogger logger)\n {\n _repository = repository;\n _notificationService = notificationService;\n _logger = logger;\n }\n \n public void ProcessOrder(Order order)\n {\n _repository.Save(order);\n _notificationService.SendConfirmation(order.CustomerEmail);\n _logger.Log($"Pedido {order.Id} processado");\n }\n}\n\npublic interface IOrderRepository { void Save(Order order); }\npublic interface INotificationService { void SendConfirmation(string email); }\npublic interface ILogger { void Log(string message); }\nAgora cada classe é responsável apenas por sua tarefa. Alterar a lógica de logging não afeta o processamento de pedidos.
\n\n2. Princípio Aberto/Fechado (OCP)
\nEssência: As classes devem estar abertas para extensão, mas fechadas para modificação. Ou seja, você deve ser capaz de adicionar novos comportamentos sem alterar o código existente.
\n\nViolando o OCP em C#
\nExemplo clássico — uma calculadora com condicionais:
\npublic class AreaCalculator\n{\n public double CalculateArea(object shape)\n {\n if (shape is Rectangle rect)\n return rect.Width * rect.Height;\n else if (shape is Circle circle)\n return Math.PI * circle.Radius * circle.Radius;\n // Adicionar uma nova forma exige alterar este método!\n throw new NotSupportedException();\n }\n}