Skip to content

Injection de dépendances en Java

Introduction générale

L’injection de dépendances (Dependency Injection – DI) est un principe visant à réduire le couplage entre classes, améliorer la testabilité et clarifier l’architecture en séparant la création des objets de leur utilisation.

DI s’inscrit dans le principe plus large d’Inversion de Contrôle (IoC).


Problème du couplage fort

Exemple problématique :

public class ServiceClient {
    private DatabaseConnection connection;

    public ServiceClient() {
        this.connection = new DatabaseConnection();
    }

    public void doSomething() {
        connection.query("SELECT * FROM clients");
    }
}

Problèmes : dépendance forte, faible testabilité, difficulté de substitution, configuration figée.


Principe de l’injection de dépendances

La dépendance est fournie par un tiers :

public class ServiceClient {
    private final DatabaseConnection connection;

    public ServiceClient(DatabaseConnection connection) {
        this.connection = connection;
    }

    public void doSomething() {
        connection.query("SELECT * FROM clients");
    }
}

Formes principales de DI

Injection par constructeur (recommandée)

public class ServiceClient {
    private final Repository repository;

    public ServiceClient(Repository repository) {
        this.repository = repository;
    }
}

Injection via setter

public class ServiceClient {
    private Repository repository;

    public void setRepository(Repository repository) {
        this.repository = repository;
    }
}

Injection par champ

Utilisée par certains frameworks (ex. Spring avec @Autowired).


DI et interfaces pour réduire le couplage

Interface :

public interface NotificationService {
    void envoyer(String message);
}

Implémentations :

public class EmailService implements NotificationService {
    public void envoyer(String msg) { System.out.println("Email: " + msg); }
}

public class SmsService implements NotificationService {
    public void envoyer(String msg) { System.out.println("SMS: " + msg); }
}

Contrôleur utilisant DI :

public class NotificationController {
    private final NotificationService service;

    public NotificationController(NotificationService service) {
        this.service = service;
    }

    public void notifier() {
        service.envoyer("Bonjour !");
    }
}

Exemple MVC utilisant DI

Interface vue :

public interface View {
    void afficher(String msg);
}

Vue console :

public class ConsoleView implements View {
    public void afficher(String msg) { System.out.println(msg); }
}

Contrôleur :

public class Controller {
    private final Bibliotheque model;
    private final View view;

    public Controller(Bibliotheque model, View view) {
        this.model = model;
        this.view = view;
    }
}

DI dans les tests unitaires

Mock de vue :

public class MockView implements View {
    private String dernierMessage;

    @Override
    public void afficher(String msg) { this.dernierMessage = msg; }

    public String getDernierMessage() { return dernierMessage; }
}

Test :

@Test
public void testAffichage() {
    Bibliotheque model = new Bibliotheque();
    MockView view = new MockView();
    Controller ctrl = new Controller(model, view);

    ctrl.ajouterLivre("Dune");

    assertEquals("Livre ajouté", view.getDernierMessage());
}

Bonnes pratiques

  • Favoriser DI par constructeur
  • Utiliser des interfaces
  • Garder les classes immuables
  • Pas de logique dans les constructeurs
  • DI ne remplace pas l’architecture, elle la supporte

Conclusion

L’injection de dépendances est un pilier du développement Java moderne.

Elle rend les systèmes modulaires, testables et évolutifs, que ce soit en Java pur, avec MVC ou au sein de frameworks comme Spring.