Introduction : Pourquoi les patrons de conception sont importants en Go
Go (Golang) est un langage compilé moderne créé par Google pour résoudre les problèmes des systèmes à forte charge. Malgré sa simplicité et sa concision, le développement en Go nécessite la compréhension des patrons de conception — des solutions éprouvées pour des problèmes typiques. Dans cet article, nous allons analyser les principaux patrons avec des exemples de code qui vous aideront à écrire un code plus propre, évolutif et maintenable.
Pourquoi est-ce important ? Go ne supporte pas la POO classique (héritage, surcharge de méthodes), mais offre des mécanismes puissants — les interfaces, la composition et les goroutines. Les patrons s'adaptent à ces particularités, ce qui les rend particulièrement efficaces.
Patrons de création (Creational)
Les patrons de création gèrent le processus de création d'objets. En Go, ils sont souvent implémentés via des fonctions constructeurs et des interfaces.
1. Singleton (Singleton)
Garantit qu'une classe n'a qu'une seule instance et fournit un point d'accès global à celle-ci. En Go, cela est particulièrement pertinent pour les configurations, les journaux et les pools de connexions.
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}Explication : sync.Once garantit que l'initialisation n'a lieu qu'une seule fois, même en cas d'accès concurrent. C'est la manière idiomatique standard d'implémenter le Singleton en Go.
2. Factory Method (Méthode de fabrique)
Définit une interface pour créer un objet, mais permet aux sous-classes de modifier le type des objets créés. En Go, cela est implémenté via des fonctions qui retournent une interface.
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" }
// Méthode de fabriquefunc 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()) // Livraison par voie maritime}3. Builder (Constructeur)
Permet de créer des objets complexes étape par étape. Idéal pour les objets avec de nombreux paramètres optionnels.
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("fine"). SetSauce("tomate"). AddTopping("fromage"). AddTopping("champignons"). Build() fmt.Printf("Pizza : %+v", pizza)}