SOLID - Balta.io
O SRP (Single Reponsibilitty Principle ou Princípio da Responsabilidade Única) afirma que uma classe deve ter apenas uma responsabilidade e uma razão para mudar.
-
Benefícios:
- Manutenção facilitada: Classes com uma responsabilidade específica são mais fáceis de entender e modificar.
- Reutilização e Testes: Classes focadas em uma única responsabilidade são mais reutilizáveis e fáceis de testar.
- Organização e Clareza: Cada classe tem um propósito claro.
- Redução de Acoplamento: Cada classe pode ser alterada sem impactar outras partes desnecessariamente.
-
Exemplo de Má Prática: Uma classe que gera um relatório, imprime e salva ao mesmo tempo. Isso viola o SRP, pois ela tem três responsabilidades distintas.
- No código, um exemplo dessa má prática pode ser encontrado em Report.cs, onde a classe contém os métodos generate(), print() e save(), acumulando múltiplas funções.
-
Exemplo de Boa Prática: Para seguir o SRP, dividimos as responsabilidades em classes especializadas:
- Report.cs responsável apenas por gerar o relatório, PrintService.cs cuida exclusivamente da impressão, ReportRepository.cs gerencia a persistência no banco de dados.
Note
Dica Final: Sempre pergunte: "Esta classe tem mais de uma razão para mudar?" Se a resposta for "sim", é hora de aplicar o SRP.
O OCP (Open/Closed Principle ou Principio do Aberto/Fechado) afirma que as classes devem estar "abertas para extensão, mas fechadas para modificação".
-
Benefícios:
- Facilidade de Extensão: Permite adicionar novas funcionalidades sem modificar o código já escrito.
- Redução de Bugs: Evita alterações em código existente, reduzindo o risco de introduzir falhas inesperadas.
- Facilidade de Manutenção: Código já existente não é alterado, evitando a introdução de novos bugs.
- Extensibilidade: Novos comportamentos são adicionados por meio de novas classes.
-
Exemplo de Má Prática: Modificar uma classe diretamente para adicionar um novo comportamento.
- No código, toda vez que um novo tipo de produto precisar de um desconto diferente, a classe Discount.cs precisará ser modificada. Isso viola o OCP, pois a classe não está fechada para modificações.
-
Exemplo de Boa Prática: Use o polimorfismo para estender o comportamento sem modificar a classe base.
- Criamos uma classe base abstrata Discount.cs e, para cada categoria de produto, criamos uma classe específica que estende essa base e implementa sua própria lógica de desconto. Como é possível visualizar nas classes: BeautyDiscount.cs, ElectronicsDiscount.cs, FashionDiscount.cs, HealthDiscount.cs.
Note
Dica Final: Pense em como tornar o código extensível. A adição de novas funcionalidades não deve significar modificar o código existente.
O LSP (Liskov Substitution Principle ou Princípio da Substituição de Liskov) afirma que objetos de uma classe derivada devem poder substituir objetos de sua classe base sem alterar a funcionalidade do programa.
-
Benefícios:
- Polimorfismo Seguro: Classes focadas em uma única responsabilidade são mais reutilizáveis e fáceis de testar.
- Polimorfismo Correto: Classes derivadas mantêm o contrato da classe base.
- Consistência e Confiabilidade: Classes com uma responsabilidade específica são mais fáceis de entender e modificar.
- Confiabilidade: Evita comportamentos inesperados ao substituir classes base por derivadas.
-
Exemplo de Má Prática: Classes derivadas que alteram ou invalidam funcionalidades da classe base.
- No primeiro exemplo de código, a classe Square.cs herda de Rectangle e altera a lógica de atribuição de Width e Height, quebrando o princípio da substituição, pois muda o comportamento esperado da classe base.
- No segundo exemplo de código, a classe SavingsAccount.cs herda de BankAccount e altera a lógica do método Withdraw, ou seja, claramente a classe derivada não consegue substituir a classe base, precisa sobrescrever o comportamento da classe base por algum motivo.
-
Exemplo de Boa Prática: A classe derivada deve poder substituir a classe base.
- Para evitar a violação do LSP no primeiro exemplo, extraímos um conceito mais genérico, Shape.cs, e fazemos com que Rectangle.cs e Square.cs implementem essa classe abstrata corretamente.
- Para evitar a violação do LSP no segundo exemplo, a classe BankAccount.cs e o método Withdraw se tornaram uma abstração delegando seu próprio comportamento onde SavingsAccount.cs e CheckingAccount.cs automaticamente conseguem substituir a classe base, aplicando o LSP.
Note
Dica Final: Certifique-se de que as classes derivadas respeitam o comportamento esperado da classe base. Pergunte-se: "Essa classe dderivada pode realmente substituir a classe base sem problemas?".
O ISP (Interface Segregation Principle ou Princípio da Segregação de Interfaces) afirma que uma interface não deve forçar classes a implementar métodos que elas não utilizam.
-
Benefícios:
- Flexibilidade e Clareza: Interfaces específicas evitam que classes implementem métodos desnecessários.
- Manuitenção e Testes Fáceis: Classes ficam mais fáceis de manter e testar, pois têm interfaces enxutas e focadas.
- Interfaces Enxutas: Classes implementam apenas métodos relevantes, evitando implementações vazias.
- Facilidade de Manutenção: Mudanças afetam menos classes, pois as interfaces são específicas.
-
Exemplo de Má Prática: Interfaces grandes que obrigam classes a implementar métodos irrelevantes para elas.
- No código, a interface IEmployee.cs obriga a classe ContractEmployee.cs a implementar CalculateBenefits() e ter métodos que não são necessários para ela. Isso viola o ISP, pois as classes são forçadas a implementar métodos que não utilizam.
-
Exemplo de Boa Prática: Segregar as interfaces.
- As interfaces IBenefitsCalculator.cs e ISalaryCalculator.cs são focadas e permitem que cada classe implemente apenas o que é necessário. Dessa forma, a classe ContractEmployee.cs implementa apenas CalculateSalary() e a classe FullTimeEmployee.cs implementa ambas CalculateSalary() e CalculateBenefits().
Note
Dica Final: Quando pojetar interfaces, pergunte-se se todas as classes realmente precisam implementar todos os métodos. Se não, é hora de segregar as interfaces e aplicar o ISP.
O DIP (Dependency Inversion Principle ou Princípio da Inversão de Dependência) afirma que módulos de alto nível não devem depender de módulos de baixo nível. Ambos devem depender de abstrações. Além disso, abstrações não devem depender de detalhes. Detalhes devem depender de abstrações.
-
Benefícios:
- Desacoplamento: Facilita a troca de implementações sem impactar o código de alto nível.
- Facilidade de Teste: Módulos podem ser testados isoladamente usando abstrações.
- Testabilidade: Permite a criação de mocks e stubs para testes unitários.
-
Exemplo de Má Prática: Um módulo de alto nível que depende diretamente de uma classe concreta de baixo nível.
- No código, a classe UserService.cs depende diretamente da classe concreta EmailService.cs dificultando qualquer mock. Isso viola o DIP, pois o módulo de alto nível depende de um módulo de baixo nível.
-
Exemplo de Boa Prática: Inverter as dependências para depender de abstrações.
- Criamos uma interface IEmailService.cs que define o contrato de envio e faz com que EmailService.cs implemente essa interface. Dessa forma, a classe UserService.cs depende apenas da abstração e não da implementação, podendo até mesmo utilizar FakeEmailService.cs.
Note
Dica Final: Verifique se as classes dde alto nível estão acopladas a classes de baixo nível. Se sim, considere introduzir abstrações para facilitar a manutenção e evolução do código.