Introdução: O debate eterno no mundo Go
\n\nGo — uma linguagem que quebrou paradigmas. Ela não perdoa complexidade excessiva, mas ao mesmo tempo dá ao desenvolvedor uma liberdade incrível. Todo mundo que começa a programar em Go (ou migra de Python ou Java) mais cedo ou mais tarde se depara com uma parede: “Qual é a maneira correta? Escrever como em Java com classes e herança ou como em Haskell com funções sem efeitos colaterais?”
\n\nSpoiler: Go não é uma linguagem puramente orientada a objetos nem puramente funcional. Ela é pragmática. Mas isso não significa que você não pode (e não deve) usar ambos os paradigmas. Neste artigo, vamos analisar como combinar a abordagem funcional e orientada a objetos em Go para que seu código seja legível, testável e rápido. Não vamos dar palestras, mas sim ver exemplos reais e antipadrões.
\n\nSe você está procurando melhores práticas em Go e quer entender como escrever código limpo nesta linguagem — está no lugar certo.
\n\n1. Abordagem orientada a objetos em Go: Sem classes, mas com alma
\n\nMuitos acreditam que, como Go não tem class, então POO não existe nele. Isso é um mito. POO em Go é construída sobre dois pilares: structs e interfaces. Não há herança, mas há composição, que os criadores da linguagem consideram mais flexível.
Structs e métodos
\nVocê pode definir métodos para qualquer tipo. Essa é a base do código orientado a objetos.
\n\npackage main\n\nimport "fmt"\n\n// Definindo uma interface (contrato)\ntype Notifier interface {\n Send(message string) error\n}\n\n// Struct implementando a interface\ntype EmailNotifier struct {\n Email string\n}\n\n// Método para EmailNotifier\nfunc (e EmailNotifier) Send(message string) error {\n fmt.Printf("Enviando email para %s: %s\\n", e.Email, message)\n return nil\n}\n\n// Outra struct\ntype SMSNotifier struct {\n Phone string\n}\n\nfunc (s SMSNotifier) Send(message string) error {\n fmt.Printf("Enviando SMS para %s: %s\\n", s.Phone, message)\n return nil\n}\n\n// Função que aceita qualquer Notifier (polimorfismo)\nfunc AlertAll(notifiers []Notifier, msg string) {\n for _, n := range notifiers {\n n.Send(msg)\n }\n}\n\nfunc main() {\n notifiers := []Notifier{\n EmailNotifier{Email: "user@example.com"},\n SMSNotifier{Phone: "+1234567890"},\n }\n AlertAll(notifiers, "Servidor caiu!")\n}\n\nVantagens da POO em Go:
\n- \n
- Polimorfismo através de interfaces. Você escreve a função
AlertAlluma vez, e ela funciona com qualquer tipo que implementeNotifier. Isso é baixo acoplamento. \n - Estado. Structs são ideais para modelos de dados (User, Order, Config). \n
- Encapsulamento. Campos com letra minúscula são privados, com maiúscula são públicos. Simples e eficaz. \n
Desvantagens:
\n- \n
- Fácil cair no “estilo Java”: criar hierarquias profundas, embora Go não as suporte. \n
- Mutabilidade (alteração de estado) dificulta testes e depuração. \n
2. Programação funcional em Go: Pureza pela velocidade
\n\nO estilo funcional em Go não é cálculo lambda, mas sim um conjunto de princípios: mínimo de efeitos colaterais, imutabilidade, funções como argumentos. Com o advento dos genéricos (Go 1.18+), esse estilo se tornou mais poderoso.
\n\nFunções puras e imutabilidade
\nA regra principal da programação funcional: a função recebe dados, retorna novos dados, mas não altera nada externamente.
\n\npackage main\n\nimport "fmt"\n\n// Ruim: mutando o slice de entrada (efeito colateral)\nfunc BadAddPrefix(users []string, prefix string) {\n for i := range users {\n users[i] = prefix + users[i]\n }\n}\n\n// Bom: retornando um novo slice (função pura)\