Patrons de conception en Go avec exemples : de A à Z
Les patrons de conception (Design Patterns) sont des solutions architecturales éprouvées qui aident les développeurs Go à créer un code flexible, évolutif et maintenable. Contrairement aux langages OOP classiques, Go propose sa propre approche unique pour implémenter les patrons via des interfaces, la composition et les goroutines. Dans cet article, nous analyserons les principaux patrons de conception en Go avec des exemples de code concrets.
1. Patrons de création (Creational Patterns)
Les patrons de création gèrent la création d'objets, rendant le système indépendant de la manière dont ils sont créés.
1.1. Singleton (Singleton)
Le singleton garantit qu'une classe n'aura qu'une seule instance. En Go, cela est souvent implémenté via sync.Once pour la sécurité des 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("Connexion unique à la base de données créée") }) return instance}
func main() { db1 := GetDatabaseInstance() db2 := GetDatabaseInstance() fmt.Println(db1 == db2) // true}1.2. Factory Method (Méthode de fabrique)
La fabrique délègue la création d'objets aux sous-classes. En Go, cela se fait à l'aide d'interfaces.
package main
import "fmt"
// Interface du produittype Transport interface { Deliver() string}
// Produits concretstype Truck struct{}func (t Truck) Deliver() string { return "Livraison par voie terrestre" }
type Ship struct{}func (s Ship) Deliver() string { return "Livraison par voie maritime" }
// Fabriquetype 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()) // Livraison par voie terrestre}2. Patrons structurels (Structural Patterns)
Les patrons structurels aident à organiser les classes et les objets en structures plus grandes.
2.1. Adapter (Adaptateur)
L'adaptateur permet à des objets avec des interfaces incompatibles de fonctionner ensemble. En Go, cela se résout via un wrapper.
package main
import "fmt"
// Ancienne interfacetype OldPrinter interface { PrintOld(data string)}
type LegacyPrinter struct{}func (l LegacyPrinter) PrintOld(data string) { fmt.Println("Legacy:", data)}
// Nouvelle interfacetype NewPrinter interface { PrintNew(data string)}
// Adaptateurtype 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 (Composite)
Le composite permet de traiter des groupes d'objets comme un seul objet. Idéal pour les structures arborescentes.
package main
import "fmt"
// Interface du composanttype Graphic interface { Draw() string}
// Élément feuilletype Circle struct{}func (c Circle) Draw() string { return "Dessiner un cercle" }
// Conteneurtype CompoundGraphic struct { children []Graphic}
func (cg *CompoundGraphic) Add(g Graphic) { cg.children = append(cg.children, g)}
func (cg *CompoundGraphic) Draw() string { result := "Figure composite :" for _, child := range cg.children { result += " - " + c