Princípios SOLID em Go: teoria e prática do A ao Z
\n\nSOLID é a sigla de cinco princípios fundamentais da programação orientada a objetos que ajudam a criar código flexível, sustentável e escalável. Embora Go não seja uma linguagem OOP clássica (não possui classes e herança no sentido tradicional), esses princípios são aplicáveis e extremamente úteis no desenvolvimento com Go. Neste artigo, analisaremos cada princípio com exemplos concretos, mostraremos como aplicá-los em Go e explicaremos por que isso é importante.
\n\n1. Single Responsibility Principle (SRP) — Princípio da Responsabilidade Única
\n\nEssência: Cada módulo, classe ou função deve ter apenas um motivo para ser alterado. Em outras palavras, uma entidade deve ser responsável por uma única parte da funcionalidade.
\n\nEm Go, isso é frequentemente violado quando uma struct realiza muitas tarefas: trabalha com banco de dados, envia e-mail, gera relatórios.
\n\nExemplo ruim (violação do SRP):
\ntype User struct {\n Name string\n Email string\n}\n\nfunc (u *User) Save() error {\n // lógica de salvar no BD\n return nil\n}\n\nfunc (u *User) SendWelcomeEmail() error {\n // lógica de envio de e-mail\n return nil\n}\n\nfunc (u *User) GenerateReport() string {\n // lógica de geração de relatório\n return "report"\n}\n\n\nExemplo bom (respeitando o SRP):
\ntype User struct {\n Name string\n Email string\n}\n\ntype UserRepository struct{}\n\nfunc (r *UserRepository) Save(u *User) error {\n // apenas salvar\n return nil\n}\n\ntype EmailService struct{}\n\nfunc (s *EmailService) SendWelcome(u *User) error {\n // apenas enviar e-mail\n return nil\n}\n\ntype ReportGenerator struct{}\n\nfunc (g *ReportGenerator) Generate(u *User) string {\n // apenas gerar relatório\n return "report"\n}\n\n\nConclusão: Cada struct é responsável por sua tarefa. Alterar a lógica de salvamento não afetará o envio de e-mails.
\n\n2. Open-Closed Principle (OCP) — Princípio do Aberto/Fechado
\n\nEssência: Entidades de software (classes, módulos, funções) devem estar abertas para extensão, mas fechadas para modificação. Ou seja, você deve ser capaz de adicionar novas funcionalidades sem alterar o código existente.
\n\nEm Go, isso é perfeitamente implementado por meio de interfaces.
\n\nExemplo ruim (violação do OCP):
\ntype PaymentProcessor struct{}\n\nfunc (p *PaymentProcessor) Process(paymentType string, amount float64) {\n if paymentType == "credit_card" {\n // lógica para cartão de crédito\n } else if paymentType == "paypal" {\n // lógica para PayPal\n }\n // ao adicionar um novo tipo, será necessário alterar este método\n}\n\n\nExemplo bom (respeitando o OCP):
\ntype PaymentMethod interface {\n Pay(amount float64) error\n}\n\ntype CreditCard struct{}\n\nfunc (c *CreditCard) Pay(amount float64) error {\n // lógica para cartão de crédito\n return nil\n}\n\ntype PayPal struct{}\n\nfunc (p *PayPal) Pay(amount float64) error {\n // lógica para PayPal\n return nil\n}\n\nfunc ProcessPayment(method PaymentMethod, amount float64) error {\n return method.Pay(amount)\n}\n\n\nConclusão: Agora, para adicionar um novo método de pagamento (por exemplo, ApplePay), basta criar uma nova struct com o método Pay(), sem tocar no código existente.
3. Liskov Substitution Principle (LSP) — Princípio da Substituição de Barbara Liskov
\n\nEssência: Os objetos em um programa devem ser substituíveis por instâncias de seus subtipos sem alterar a correção da execução do programa. Em Go, isso significa que, se uma função aceita uma interface, qualquer implementação dessa interface deve funcionar corretamente, sem quebrar as expectativas.
\n\nExemplo ruim (violação do LSP):
\ntype Bird interface {\n Fly() error\n}\n\nt