SOLID Principles para .NET Senior
📋 Introdução
SOLID é um acrônimo para os cinco princípios fundamentais do design de software orientado a objetos, criados por Robert C. Martin.
🎯 Os 5 Princípios
1. S - Single Responsibility Principle (SRP)
Definição: Uma classe deve ter apenas uma razão para mudar.
❌ Violação do SRP
csharp
public class UserService
{
public void CreateUser(User user)
{
ValidateUser(user);
SaveUser(user);
SendWelcomeEmail(user);
LogUserCreation(user);
}
private void ValidateUser(User user) { }
private void SaveUser(User user) { }
private void SendWelcomeEmail(User user) { }
private void LogUserCreation(User user) { }
}✅ Aplicando SRP
csharp
public class UserService
{
private readonly IUserRepository _userRepository;
private readonly IEmailService _emailService;
private readonly ILogger _logger;
public UserService(IUserRepository userRepository,
IEmailService emailService,
ILogger logger)
{
_userRepository = userRepository;
_emailService = emailService;
_logger = logger;
}
public void CreateUser(User user)
{
_userRepository.Save(user);
_emailService.SendWelcomeEmail(user);
_logger.LogUserCreation(user);
}
}2. O - Open/Closed Principle (OCP)
Definição: Entidades devem estar abertas para extensão, mas fechadas para modificação.
❌ Violação do OCP
csharp
public class DiscountCalculator
{
public decimal CalculateDiscount(Order order)
{
if (order.CustomerType == CustomerType.Regular)
return order.Total * 0.1m;
else if (order.CustomerType == CustomerType.Premium)
return order.Total * 0.2m;
else if (order.CustomerType == CustomerType.VIP)
return order.Total * 0.3m;
return 0;
}
}✅ Aplicando OCP
csharp
public abstract class DiscountStrategy
{
public abstract decimal CalculateDiscount(Order order);
}
public class RegularCustomerDiscount : DiscountStrategy
{
public override decimal CalculateDiscount(Order order) => order.Total * 0.1m;
}
public class PremiumCustomerDiscount : DiscountStrategy
{
public override decimal CalculateDiscount(Order order) => order.Total * 0.2m;
}
public class DiscountCalculator
{
private readonly Dictionary<CustomerType, DiscountStrategy> _strategies;
public DiscountCalculator()
{
_strategies = new Dictionary<CustomerType, DiscountStrategy>
{
{ CustomerType.Regular, new RegularCustomerDiscount() },
{ CustomerType.Premium, new PremiumCustomerDiscount() }
};
}
public decimal CalculateDiscount(Order order)
{
if (_strategies.TryGetValue(order.CustomerType, out var strategy))
return strategy.CalculateDiscount(order);
return 0;
}
}3. L - Liskov Substitution Principle (LSP)
Definição: Objetos de uma superclasse devem poder ser substituídos por objetos de suas subclasses sem quebrar a aplicação.
❌ Violação do LSP
csharp
public class Rectangle
{
public virtual int Width { get; set; }
public virtual int Height { get; set; }
public virtual int Area => Width * Height;
}
public class Square : Rectangle
{
public override int Width
{
get => base.Width;
set
{
base.Width = value;
base.Height = value;
}
}
public override int Height
{
get => base.Height;
set
{
base.Width = value;
base.Height = value;
}
}
}✅ Aplicando LSP
csharp
public abstract class Shape
{
public abstract int Area { get; }
}
public class Rectangle : Shape
{
public int Width { get; set; }
public int Height { get; set; }
public override int Area => Width * Height;
}
public class Square : Shape
{
public int Side { get; set; }
public override int Area => Side * Side;
}4. I - Interface Segregation Principle (ISP)
Definição: Muitas interfaces específicas são melhores que uma interface geral.
❌ Violação do ISP
csharp
public interface IWorker
{
void Work();
void Eat();
void Sleep();
}
public class Human : IWorker
{
public void Work() { }
public void Eat() { }
public void Sleep() { }
}
public class Robot : IWorker
{
public void Work() { }
public void Eat() { throw new NotImplementedException(); }
public void Sleep() { throw new NotImplementedException(); }
}✅ Aplicando ISP
csharp
public interface IWorkable
{
void Work();
}
public interface IEatable
{
void Eat();
}
public interface ISleepable
{
void Sleep();
}
public class Human : IWorkable, IEatable, ISleepable
{
public void Work() { }
public void Eat() { }
public void Sleep() { }
}
public class Robot : IWorkable
{
public void Work() { }
}5. D - Dependency Inversion Principle (DIP)
Definição: Dependa de abstrações, não de implementações concretas.
❌ Violação do DIP
csharp
public class EmailNotifier
{
public void SendNotification(string message) { }
}
public class OrderService
{
private readonly EmailNotifier _emailNotifier;
public OrderService()
{
_emailNotifier = new EmailNotifier();
}
public void ProcessOrder(Order order)
{
_emailNotifier.SendNotification("Order processed");
}
}✅ Aplicando DIP
csharp
public interface INotifier
{
void SendNotification(string message);
}
public class EmailNotifier : INotifier
{
public void SendNotification(string message) { }
}
public class SmsNotifier : INotifier
{
public void SendNotification(string message) { }
}
public class OrderService
{
private readonly INotifier _notifier;
public OrderService(INotifier notifier)
{
_notifier = notifier;
}
public void ProcessOrder(Order order)
{
_notifier.SendNotification("Order processed");
}
}🏗️ Aplicação Prática em .NET
Dependency Injection Container
csharp
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IUserRepository, UserRepository>();
services.AddScoped<IEmailService, EmailService>();
services.AddScoped<ILogger, Logger>();
services.AddScoped<IUserService, UserService>();
}
}Repository Pattern com SOLID
csharp
public interface IRepository<T> where T : class
{
Task<T> GetByIdAsync(int id);
Task<IEnumerable<T>> GetAllAsync();
Task AddAsync(T entity);
Task UpdateAsync(T entity);
Task DeleteAsync(int id);
}
public interface IUserRepository : IRepository<User>
{
Task<User> GetByEmailAsync(string email);
}
public class UserRepository : IUserRepository
{
private readonly DbContext _context;
public UserRepository(DbContext context) => _context = context;
public async Task<User> GetByIdAsync(int id)
{
return await _context.Users.FindAsync(id);
}
public async Task<User> GetByEmailAsync(string email)
{
return await _context.Users.FirstOrDefaultAsync(u => u.Email == email);
}
}📚 Benefícios dos Princípios SOLID
1. Manutenibilidade
- Código mais fácil de entender e modificar
- Mudanças isoladas em componentes específicos
2. Testabilidade
- Classes menores e mais focadas
- Dependências injetadas facilitam mocking
3. Extensibilidade
- Novas funcionalidades sem modificar código existente
- Polimorfismo e herança bem utilizados
4. Reutilização
- Componentes modulares e independentes
- Interfaces bem definidas
⚠️ Pontos de Atenção
- Não force abstrações: Use apenas quando necessário
- Mantenha simplicidade: Over-engineering pode complicar
- Considere o contexto: Nem sempre todos os princípios se aplicam
- Performance: Abstrações podem impactar performance
- Complexidade: Muitas interfaces podem aumentar complexidade
📖 Recursos Adicionais
Livros Recomendados
- "Clean Code" - Robert C. Martin
- "Agile Principles, Patterns, and Practices" - Robert C. Martin
- "Refactoring" - Martin Fowler
Artigos e Blogs
- Martin Fowler's Blog
- Uncle Bob's Blog
- Microsoft .NET Documentation