Introdução: por que os princípios SOLID são necessários em C#?
Todo desenvolvedor C# mais cedo ou mais tarde enfrenta o problema do "código sujo": classes que fazem tudo de uma vez, dependências ilegíveis e a impossibilidade de fazer alterações sem o risco de quebrar todo o projeto. SOLID é um acrônimo de cinco princípios fundamentais de design orientado a objetos, formulados por Robert Martin. Eles ajudam a criar código flexível, sustentável e testável em C# (.NET).
Neste artigo, abordaremos cada princípio SOLID com base teórica e exemplos práticos em C#. Você aprenderá como aplicá-los em projetos reais, evitando erros comuns.
1. Single Responsibility Principle (SRP) — Princípio da Responsabilidade Única
Teoria
Uma classe deve ter apenas um motivo para mudar. Em outras palavras, cada classe é responsável por apenas uma parte da funcionalidade do programa. A violação do SRP faz com que a alteração de uma operação (por exemplo, salvar no banco de dados) afete a lógica de cálculos ou exibição.
Prática em C#
Vamos considerar um exemplo típico de violação do SRP — uma classe que simultaneamente calcula o salário e salva o funcionário no banco de dados:
// ❌ Violação do SRP: a classe faz demaispublic class Employee{ public string Name { get; set; } public decimal Salary { get; set; }
public decimal CalculateBonus() { return Salary * 0.1m; }
public void SaveToDatabase() { // Lógica de salvar no BD Console.WriteLine("Funcionário salvo no BD"); }}Vamos corrigir a situação separando as responsabilidades:
// ✅ SRP: cada classe é responsável por sua tarefapublic class Employee{ public string Name { get; set; } public decimal Salary { get; set; }}
public class BonusCalculator{ public decimal Calculate(Employee employee) { return employee.Salary * 0.1m; }}
public class EmployeeRepository{ public void Save(Employee employee) { Console.WriteLine("Funcionário salvo no BD"); }}2. Open-Closed Principle (OCP) — Princípio Aberto/Fechado
Teoria
Entidades de software (classes, módulos, funções) devem estar abertas para extensão, mas fechadas para modificação. Isso significa que você pode adicionar novas funcionalidades sem alterar o código existente.
Prática em C#
Suponha que precisamos calcular a área de diferentes formas. A violação do OCP é usar if-else ou switch para cada novo tipo de forma:
// ❌ Violação do OCP: ao adicionar uma nova forma, é preciso alterar o códigopublic class AreaCalculator{ public double Calculate(object shape) { if (shape is Circle circle) return Math.PI * circle.Radius * circle.Radius; else if (shape is Rectangle rect) return rect.Width * rect.Height; // Ao adicionar Triangle, será necessário escrever mais um if return 0; }}Solução — usar abstração e polimorfismo:
// ✅ OCP: estendemos através de herança, sem tocar no código existentepublic abstract class Shape{ public abstract double GetArea();}
public class Circle : Shape{ public double Radius { get; set; } public override double GetArea() => Math.PI * Radius * Radius;}
public class Rectangle : Shape{ public double Width { get; set; } public double Height { get; set; } public override double GetArea() => Width * Height;}
public class AreaCalculator{ public double Calculate(Shape shape) { return shape.GetArea(); // Funciona para qualquer forma }}3. Liskov Substitution Principle (LSP) — Princípio da Substituição de Barbara Liskov
Teoria
Objetos de uma classe base devem ser substituíveis por objetos de suas subclasses sem quebrar o programa.