Padrões de Projeto em Go com exemplos: de A a Z
Padrões de Projeto (Design Patterns) são soluções arquiteturais testadas pelo tempo que ajudam desenvolvedores Go a criar código flexível, escalável e de fácil manutenção. Diferente das linguagens OOP clássicas, o Go oferece sua própria abordagem única para implementar padrões através de interfaces, composição e goroutines. Neste artigo, analisaremos os principais padrões de projeto em Go com exemplos concretos de código.
1. Padrões Criacionais (Creational Patterns)
Os padrões criacionais são responsáveis pela criação de objetos, tornando o sistema independente da forma como eles são criados.
1.1. Singleton (Singleton)
O Singleton garante que uma classe tenha apenas uma instância. Em Go, isso é frequentemente implementado com sync.Once para segurança em threads.
package main
import ( "fmt" "sync")
type Database struct { connection string}
var ( instance *Database once sync.Once)
func GetDatabaseInstance() *Database { once.Do(func() { instance = &Database{connection: "postgres://localhost:5432/mydb"} fmt.Println("Criada conexão única com o banco de dados") }) return instance}
func main() { db1 := GetDatabaseInstance() db2 := GetDatabaseInstance() fmt.Println(db1 == db2) // true}1.2. Factory Method (Método Fábrica)
A fábrica delega a criação de objetos para subclasses. Em Go, isso é feito com interfaces.
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" }
// Fábricatype LogisticsFactory interface { CreateTransport() Transport}
type RoadLogistics struct{}func (r RoadLogistics) CreateTransport() Transport { return Truck{} }
type SeaLogistics struct{}func (s SeaLogistics) CreateTransport() Transport { return Ship{} }
func main() { var factory LogisticsFactory = RoadLogistics{} transport := factory.CreateTransport() fmt.Println(transport.Deliver()) // Entrega por terra}2. Padrões Estruturais (Structural Patterns)
Os padrões estruturais ajudam a organizar classes e objetos em estruturas maiores.
2.1. Adapter (Adaptador)
O Adaptador permite que objetos com interfaces incompatíveis trabalhem juntos. Em Go, isso é resolvido através de um wrapper.
package main
import "fmt"
// Interface antigatype OldPrinter interface { PrintOld(data string)}
type LegacyPrinter struct{}func (l LegacyPrinter) PrintOld(data string) { fmt.Println("Legacy:", data)}
// Nova interfacetype NewPrinter interface { PrintNew(data string)}
// Adaptadortype PrinterAdapter struct { OldPrinter OldPrinter}
func (a PrinterAdapter) PrintNew(data string) { a.OldPrinter.PrintOld(data)}
func main() { legacy := LegacyPrinter{} adapter := PrinterAdapter{OldPrinter: legacy} adapter.PrintNew("Hello, Go!") // Legacy: Hello, Go!}2.2. Composite (Compositor)
O Compositor permite tratar grupos de objetos como um único objeto. Ideal para estruturas em árvore.
package main
import "fmt"
// Interface do componentetype Graphic interface { Draw() string}
// Elemento folhatype Circle struct{}func (c Circle) Draw() string { return "Desenhando círculo" }
// Contêinertype CompoundGraphic struct { children []Graphic}
func (cg *CompoundGraphic) Add(g Graphic) { cg.children = append(cg.children, g)}
func (cg *CompoundGraphic) Draw() string { result := "Figura composta:" for _, child := range cg.children { result += " - " + c