Introdução: Por que os padrões de projeto são importantes em Go
Go (Golang) é uma linguagem compilada moderna, criada no Google para resolver problemas de sistemas de alta carga. Apesar de sua simplicidade e concisão, o desenvolvimento em Go requer a compreensão de padrões de projeto — soluções comprovadas para problemas típicos. Neste artigo, analisaremos os principais padrões com exemplos de código que o ajudarão a escrever um código mais limpo, escalável e sustentável.
Por que isso é importante? Go não suporta POO clássica (herança, sobrecarga de métodos), mas oferece mecanismos poderosos — interfaces, composição e goroutines. Os padrões se adaptam a essas características, tornando-os especialmente eficazes.
Padrões Criacionais (Creational)
Os padrões criacionais gerenciam o processo de criação de objetos. Em Go, eles são frequentemente implementados por meio de funções construtoras e interfaces.
1. Singleton (Singleton)
Garante que uma classe tenha apenas uma instância e fornece um ponto de acesso global a ela. Em Go, isso é especialmente relevante para configurações, loggers e pools de conexão.
package main
import ( "fmt" "sync")
type Config struct { Port int Host string}
var ( instance *Config once sync.Once)
func GetConfig() *Config { once.Do(func() { instance = &Config{ Port: 8080, Host: "localhost", } }) return instance}
func main() { cfg1 := GetConfig() cfg2 := GetConfig() fmt.Println(cfg1 == cfg2) // true}Explicação: sync.Once garante que a inicialização ocorra apenas uma vez, mesmo com acesso concorrente. Esta é a forma idiomática padrão de implementar o Singleton em Go.
2. Factory Method (Método Fábrica)
Define uma interface para criar um objeto, mas permite que as subclasses alterem o tipo de objetos criados. Em Go, é implementado por meio de funções que retornam uma interface.
package main
import "fmt"
// Interface do produtotype Transport interface { Deliver() string}
// Produtos concretostype Truck struct{}func (t *Truck) Deliver() string { return "Entrega por terra" }
type Ship struct{}func (s *Ship) Deliver() string { return "Entrega por mar" }
// Método Fábricafunc CreateTransport(transportType string) Transport { switch transportType { case "land": return &Truck{} case "sea": return &Ship{} default: return nil }}
func main() { transport := CreateTransport("sea") fmt.Println(transport.Deliver()) // Entrega por mar}3. Builder (Construtor)
Permite criar objetos complexos passo a passo. É ideal para objetos com muitos parâmetros opcionais.
package main
import "fmt"
type Pizza struct { Dough string Sauce string Toppings []string}
type PizzaBuilder struct { dough string sauce string toppings []string}
func NewPizzaBuilder() *PizzaBuilder { return &PizzaBuilder{}}
func (b *PizzaBuilder) SetDough(dough string) *PizzaBuilder { b.dough = dough return b}
func (b *PizzaBuilder) SetSauce(sauce string) *PizzaBuilder { b.sauce = sauce return b}
func (b *PizzaBuilder) AddTopping(topping string) *PizzaBuilder { b.toppings = append(b.toppings, topping) return b}
func (b *PizzaBuilder) Build() *Pizza { return &Pizza{ Dough: b.dough, Sauce: b.sauce, Toppings: b.toppings, }}
func main() { pizza := NewPizzaBuilder(). SetDough("fina"). SetSauce("tomate"). AddTopping("queijo"). AddTopping("cogumelos"). Build() fmt.Printf("Pizza: %+v", pizza)}