Conteneur d'injection de dépendances en C++ : bibliothèque Fruit pour l'injection de dépendances

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

Conteneur d'injection de dépendances pour C++ : présentation de la bibliothèque Fruit



Dans le développement C++ moderne, la gestion des dépendances entre classes devient cruciale pour créer des applications flexibles, testables et maintenables. Le modèle Dependency Injection (DI) permet de déplacer la création d'objets en dehors des classes, et le conteneur DI automatise ce processus. L'une des meilleures bibliothèques pour implémenter l'injection de dépendances en C++ est Fruit — un conteneur léger, rapide et type-safe fonctionnant à la compilation.



Qu'est-ce qu'un conteneur d'injection de dépendances ?

Un conteneur d'injection de dépendances est un outil qui prend en charge la création et la liaison des objets (dépendances). Au lieu que la classe crée elle-même ses dépendances via new ou des fabriques, le conteneur les « injecte » de l'extérieur. Cela implémente le principe d'Inversion de Contrôle (IoC) et rend le code faiblement couplé.



Pourquoi Fruit ?

La bibliothèque Fruit (https://github.com/google/fruit) a été développée chez Google et se distingue des alternatives (comme Boost.DI) par les caractéristiques suivantes :

  • Vérification à la compilation : toutes les liaisons sont vérifiées à la compilation, ce qui élimine les erreurs d'exécution liées à l'absence de dépendances.
  • Surcharge minimale : Fruit n'utilise pas RTTI ni d'appels virtuels, générant un code efficace.
  • Simplicité : nécessite uniquement C++14 et des fichiers d'en-tête — pas de macros ni de génération de code.
  • Support des composants : possibilité de construire un graphe de dépendances à partir de modules réutilisables.


Installation

Fruit est distribué comme une bibliothèque header-only (nécessite uniquement fruit.h). Pour l'installation via vcpkg :

vcpkg install fruit

Ou clonez le dépôt et incluez le fichier include/fruit/fruit.h.



Concepts principaux

Fruit utilise trois éléments clés :

  • Injecteur (Injector) — le conteneur qui crée les objets.
  • Composant (Component) — un module qui enregistre les types et leurs fabriques.
  • Annotations — pour distinguer différentes implémentations d'une même interface.


Exemple d'utilisation

Considérons un exemple classique : un service dépend d'un dépôt. Créons une interface et deux implémentations :



// interfaces.hpp#include <fruit/fruit.h>#include <string>

class ILogger {public: virtual void log(const std::string& msg) = 0; virtual ~ILogger() = default;};

class IUserRepository {public: virtual std::string getUser(int id) = 0; virtual ~IUserRepository() = default;};


Implémentations :

// implementations.cpp#include "interfaces.hpp"#include <iostream>

class ConsoleLogger : public ILogger {public: INJECT(ConsoleLogger()) = default; // Annotation Fruit pour le constructeur void log(const std::string& msg) override { std::cout << "[LOG] " << msg << std::endl; }};

class SqlRepository : public IUserRepository {public: INJECT(SqlRepository()) = default; std::string getUser(int id) override { return "User_" + std::to_string(id); }};


Service utilisant les deux dépendances :

// user_service.cpp#include "interfaces.hpp"

class UserService {private: ILogger* logger; IUserRepository* repo;public: INJECT(UserService(ILogger* logger, IUserRepository* repo)) : logger(logger), repo(repo) {} void processUser(int id) { auto name = repo->getUser(id); logger->log("Processing user: " + name); }};


Maintenant, assemblons le composant

Recommendations