Dependency Injection Container für C++: Überblick und praktische Beispiele
\n\nIn der modernen C++-Entwicklung werden zunehmend die Prinzipien Inversion of Control (IoC) und Dependency Injection (DI) angewendet. Diese Entwurfsmuster ermöglichen die Erstellung von lose gekoppeltem, testbarem und leicht erweiterbarem Code. Im Gegensatz zu Sprachen mit verwalteter Speicherverwaltung (Java, C#) verfügt C++ nicht über eine eingebaute Reflexion, daher erfordert die Implementierung von DI-Containern hier Template-Metaprogrammierung und die Verwendung moderner Standards (C++11/14/17/20).
\n\nIn diesem Artikel betrachten wir drei gängige Ansätze zur Abhängigkeitsinjektion in C++: die Bibliotheken Boost.DI, Google Fruit und Kangaru, und schreiben zum Verständnis der Mechanik auch einen eigenen einfachen Container.
\n\nWas ist Dependency Injection?
\n\nDependency Injection ist eine Technik, bei der ein Objekt seine Abhängigkeiten von außen erhält, anstatt sie selbst zu erstellen. Die wichtigsten Arten von DI sind:
\n- \n
- Constructor Injection – Abhängigkeiten werden über den Konstruktor übergeben. \n
- Setter Injection – Abhängigkeiten werden über Setter gesetzt. \n
- Interface Injection – Abhängigkeiten werden über eine Schnittstelle übergeben. \n
Beispiel ohne DI (feste Kopplung):
\nclass Logger {\npublic:\n void log(const std::string& msg) { /* Schreiben in Datei */ }\n};\n\nclass Service {\n Logger logger; // Feste Abhängigkeit\npublic:\n void doWork() { logger.log("Arbeit erledigt"); }\n};\n\nBeispiel mit DI (lose Kopplung):
\nclass ILogger {\npublic:\n virtual ~ILogger() = default;\n virtual void log(const std::string& msg) = 0;\n};\n\nclass ConsoleLogger : public ILogger {\npublic:\n void log(const std::string& msg) override { std::cout << msg << std::endl; }\n};\n\nclass Service {\n std::shared_ptr<ILogger> logger;\npublic:\n explicit Service(std::shared_ptr<ILogger> l) : logger(std::move(l)) {}\n void doWork() { logger->log("Arbeit erledigt"); }\n};\n\nÜberblick über DI-Container für C++
\n\n1. Boost.DI
\nBoost.DI ist eine der beliebtesten Bibliotheken. Sie verwendet C++14/17-Templates zur automatischen Auflösung von Abhängigkeiten. Unterstützt Polymorphie, Gültigkeitsbereiche (Singleton, Unique) und modulare Konfiguration.
\n\nAnwendungsbeispiel:
\n#include <boost/di.hpp>\n#include <iostream>\n\nnamespace di = boost::di;\n\nclass IWriter {\npublic:\n virtual ~IWriter() = default;\n virtual void write(const std::string&) = 0;\n};\n\nclass ConsoleWriter : public IWriter {\npublic:\n void write(const std::string& msg) override {\n std::cout << msg << std::endl;\n }\n};\n\nclass App {\n std::shared_ptr<IWriter> writer;\npublic:\n explicit App(std::shared_ptr<IWriter> w) : writer(std::move(w)) {}\n void run() { writer->write("Hallo von Boost.DI!"); }\n};\n\nint main() {\n auto injector = di::make_injector(\n di::bind<IWriter>.to<ConsoleWriter>()\n );\n auto app = injector.create<std::unique_ptr<App>>();\n app->run();\n return 0;\n}\n\nVorteile: starke Typisierung, minimaler Laufzeit-Overhead, Unterstützung von C++14/17.
\nNachteile: komplexe Compiler-Fehlermeldungen, längere Build-Zeiten.
\n\n2. Google Fruit
\nGoogle Fruit ist ein Container von Google, der auf C++11 basiert. Verwendet Makros zur Registrierung von Komponenten und unterstützt die Injektion über den Konstruktor.
\n\nBeispiel:
\n#include <fruit/fruit.h>\n#include <iostream>\n\nclass IWriter {\npublic:\n virtual void write(const std::string&) = 0;\n};\n\nclass ConsoleWriter : public IWriter {\npubli Auch in der Bibliothek
PDF-Generierung in Java: Überblick über die Bibliotheken iText, Apache PDFBox, OpenPDF
Winston für JavaScript (Node.js): Vollständige Anleitung mit Beispielen
Event Bus und das Observer-Pattern in Swift (iOS/macOS)
Internationalisierung (i18n) in C++: Vollständiger Leitfaden