SOLID Flashcards
Single Responsibility Principle (Princípio da Responsabilidade Única)
Contexto: Cada classe deve ter uma única responsabilidade. No contexto de um banco, podemos separar as responsabilidades de gerenciar contas, clientes e transações.
Explicação: A classe Conta é responsável apenas por gerenciar o saldo e as operações de depósito e saque. A classe TransacaoService é responsável por registrar as transações. Dessa forma, cada classe tem uma única responsabilidade.
public class Conta { public string NumeroConta { get; set; } public decimal Saldo { get; set; } public void Depositar(decimal valor) { Saldo += valor; } public void Sacar(decimal valor) { if (Saldo >= valor) { Saldo -= valor; } else { throw new InvalidOperationException("Saldo insuficiente"); } } } public class TransacaoService { public void RegistrarTransacao(Conta conta, decimal valor, string tipo) { // Lógica para registrar a transação no banco de dados Console.WriteLine($"Transação registrada: {tipo} de {valor} na conta {conta.NumeroConta}"); } }
Open/Closed Principle (Princípio Aberto/Fechado)
Contexto: O sistema deve ser aberto para extensão, mas fechado para modificação. Podemos estender o comportamento do sistema sem modificar o código existente.
Explicação: A classe Conta está fechada para modificação, mas aberta para extensão. Podemos adicionar novos tipos de contas (como ContaInvestimento) sem modificar a classe Conta.
public abstract class Conta { public string NumeroConta { get; set; } public decimal Saldo { get; set; } public abstract void AplicarTaxa(); } public class ContaCorrente : Conta { public override void AplicarTaxa() { Saldo -= 10; // Taxa mensal para conta corrente } } public class ContaPoupanca : Conta { public override void AplicarTaxa() { Saldo *= 1.005m; // Juros mensais para conta poupança } }
Liskov Substitution Principle (Princípio da Substituição de Liskov)
Contexto: Objetos de uma classe derivada devem ser capazes de substituir objetos da classe base sem alterar a corretude do programa.
Explicação: A classe ContaCorrente pode substituir a classe Conta sem alterar o comportamento esperado. O método Sacar foi sobrescrito para considerar o limite do cheque especial.
public class Conta { public string NumeroConta { get; set; } public decimal Saldo { get; set; } public virtual void Sacar(decimal valor) { if (Saldo >= valor) { Saldo -= valor; } else { throw new InvalidOperationException("Saldo insuficiente"); } } } public class ContaCorrente : Conta { public decimal LimiteChequeEspecial { get; set; } public override void Sacar(decimal valor) { if (Saldo + LimiteChequeEspecial >= valor) { Saldo -= valor; } else { throw new InvalidOperationException("Saldo e limite insuficientes"); } } }
Interface Segregation Principle (Princípio da Segregação de Interfaces)
Contexto: Uma classe não deve ser forçada a implementar interfaces que não utiliza. No contexto de um banco, podemos segregar interfaces para diferentes tipos de contas.
Explicação: A interface IContaBasica contém métodos básicos para depósito e saque, enquanto IContaInvestimento contém métodos específicos para investimentos. A classe ContaCorrente implementa apenas IContaBasica, enquanto ContaInvestimento implementa ambas as interfaces.
public interface IContaBasica { void Depositar(decimal valor); void Sacar(decimal valor); } public interface IContaInvestimento { void AplicarInvestimento(decimal valor); } public class ContaCorrente : IContaBasica { public decimal Saldo { get; set; } public void Depositar(decimal valor) { Saldo += valor; } public void Sacar(decimal valor) { if (Saldo >= valor) { Saldo -= valor; } else { throw new InvalidOperationException("Saldo insuficiente"); } } } public class ContaInvestimento : IContaBasica, IContaInvestimento { public decimal Saldo { get; set; } public void Depositar(decimal valor) { Saldo += valor; } public void Sacar(decimal valor) { if (Saldo >= valor) { Saldo -= valor; } else { throw new InvalidOperationException("Saldo insuficiente"); } } public void AplicarInvestimento(decimal valor) { Saldo += valor * 1.1m; // Aplica um rendimento de 10% } }
Dependency Inversion Principle (Princípio da Inversão de Dependência)
Contexto: Módulos de alto nível não devem depender de módulos de baixo nível. Ambos devem depender de abstrações. No contexto de um banco, podemos usar injeção de dependência para desacoplar as dependências.
Explicação: A classe ContaService depende da abstração ITransacaoService, e não de uma implementação concreta. Isso permite que diferentes serviços de transação sejam injetados sem alterar a classe ContaService.
public interface ITransacaoService { void RegistrarTransacao(Conta conta, decimal valor, string tipo); } public class TransacaoService : ITransacaoService { public void RegistrarTransacao(Conta conta, decimal valor, string tipo) { // Lógica para registrar a transação no banco de dados Console.WriteLine($"Transação registrada: {tipo} de {valor} na conta {conta.NumeroConta}"); } } public class ContaService { private readonly ITransacaoService _transacaoService; public ContaService(ITransacaoService transacaoService) { _transacaoService = transacaoService; } public void Depositar(Conta conta, decimal valor) { conta.Depositar(valor); _transacaoService.RegistrarTransacao(conta, valor, "Depósito"); } public void Sacar(Conta conta, decimal valor) { conta.Sacar(valor); _transacaoService.RegistrarTransacao(conta, valor, "Saque"); } }
SOLID
SOLID é um acrônimo que representa cinco princípios de design de software que visam melhorar a qualidade, a manutenibilidade e a escalabilidade do código. Esses princípios foram popularizados por Robert C. Martin (também conhecido como Uncle Bob) e são amplamente utilizados na programação orientada a objetos.
Os princípios SOLID são diretrizes valiosas para criar software robusto, flexível e de fácil manutenção. Ao aplicá-los, você pode evitar muitos problemas comuns de design de software, como código rígido, frágil e difícil de entender. Em C#, esses princípios podem ser implementados usando classes, interfaces, herança e injeção de dependência, entre outras técnicas.