Injection de Dépendances en Go : pour débutants — exemples et principes

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Injection de Dépendances en Go : pour débutants



L'Injection de Dépendances (DI), ou injection de dépendances, est l'un des patrons de conception clés qui aide à écrire un code propre, testable et maintenable. Dans le monde de Go, où la simplicité et la clarté sont primordiales, la DI permet d'éviter la création d'« objets divins » et de couplages rigides entre les composants. Dans cet article, nous allons voir ce qu'est l'Injection de Dépendances en Go, pourquoi elle est nécessaire, et comment l'appliquer en pratique à l'aide d'exemples simples et compréhensibles.



Qu'est-ce que l'Injection de Dépendances ?



Imaginez que vous écrivez un programme qui envoie des e-mails de bienvenue aux utilisateurs. Votre code ressemblerait probablement à ceci :



package main

import "fmt"

type EmailService struct{}

func (e *EmailService) SendWelcomeEmail(userEmail string) { fmt.Printf("Envoi d'un e-mail à %s", userEmail)}

type UserController struct { emailService *EmailService}

func NewUserController() *UserController { return &UserController{ emailService: &EmailService{}, // Couplage rigide }}

func (uc *UserController) RegisterUser(email string) { // ... logique d'inscription uc.emailService.SendWelcomeEmail(email)}

func main() { controller := NewUserController() controller.RegisterUser("user@example.com")}


Le problème de ce code est que UserController crée lui-même une instance de EmailService. C'est ce qu'on appelle un couplage rigide. Si vous souhaitez remplacer EmailService par, par exemple, SmsService ou utiliser un objet factice pour les tests, vous devrez modifier le code à l'intérieur de UserController.



L'Injection de Dépendances résout ce problème. Au lieu de créer les dépendances à l'intérieur de la structure, nous les « injectons » de l'extérieur. L'idée principale : « Ne demande pas ce dont tu as besoin. Dis ce dont tu as besoin et reçois-le tout prêt ».



Injection manuelle de dépendances via le constructeur



La méthode la plus simple et la plus courante de DI en Go est de passer les dépendances via une fonction constructeur. Réécrivons notre exemple :



package main

import "fmt"

// Définir une interface pour la flexibilitétype Notifier interface { Send(to string, message string) error}

type EmailService struct{}

func (e *EmailService) Send(to string, message string) error { fmt.Printf("E-mail à %s : %s", to, message) return nil}

type SmsService struct{}

func (s *SmsService) Send(to string, message string) error { fmt.Printf("SMS à %s : %s", to, message) return nil}

type UserController struct { notifier Notifier // Dépendance via interface}

// NewUserController — constructeur qui accepte la dépendancefunc NewUserController(n Notifier) *UserController { return &UserController{ notifier: n, }}

func (uc *UserController) RegisterUser(email string) { // ... logique métier uc.notifier.Send(email, "Bienvenue !")}

func main() { // Injection manuelle de la dépendance emailNotifier := &EmailService{} controller := NewUserController(emailNotifier) controller.RegisterUser("user@example.com")

// Changement facile d'implémentation smsNotifier := &SmsService{} controller2 := NewUserController(smsNotifier) controller2.RegisterUser("+123456789")}


Qu'est-ce qui a changé ?

  • Nous avons défini l'interface Notifier. Désormais, UserController ne sait pas quel service est utilisé — Email ou SMS.
  • Le constructeur NewUserController accepte Notifier comme argument. C'est ce qu'on appelle l'injection de dépendance.
  • Dans main, nous décidons nous-mêmes quelle implémentation passer.


Cette approche rend le code flexible, testable et conforme au principe d'Inversion de Contrôle.

Blogs

Book Recommendations