Contêiner de Dependency Injection para C++: visão geral e exemplos práticos
No desenvolvimento moderno em C++, os princípios de Inversion of Control (IoC) e Dependency Injection (DI) são cada vez mais aplicados. Esses padrões permitem criar código fracamente acoplado, testável e facilmente extensível. Ao contrário de linguagens com gerenciamento de memória (Java, C#), C++ não possui reflexão embutida, portanto, a implementação de contêineres DI aqui requer metaprogramação de modelos e o uso de padrões modernos (C++11/14/17/20).
Neste artigo, examinaremos três abordagens populares para injeção de dependência em C++: as bibliotecas Boost.DI, Google Fruit e Kangaru, além de escrever nosso próprio contêiner simples para entender a mecânica.
O que é Dependency Injection?
Dependency Injection é uma técnica na qual um objeto recebe suas dependências externamente, em vez de criá-las ele mesmo. Os principais tipos de DI são:
- Constructor Injection — as dependências são passadas através do construtor.
- Setter Injection — as dependências são definidas através de setters.
- Interface Injection — as dependências são passadas através de uma interface.
Exemplo sem DI (acoplamento rígido):
class Logger {public: void log(const std::string& msg) { /* gravação em arquivo */ }};
class Service { Logger logger; // Dependência rígidapublic: void doWork() { logger.log("Work done"); }};Exemplo com DI (acoplamento fraco):
class ILogger {public: virtual ~ILogger() = default; virtual void log(const std::string& msg) = 0;};
class ConsoleLogger : public ILogger {public: void log(const std::string& msg) override { std::cout << msg << std::endl; }};
class Service { std::shared_ptr<ILogger> logger;public: explicit Service(std::shared_ptr<ILogger> l) : logger(std::move(l)) {} void doWork() { logger->log("Work done"); }};Visão geral dos contêineres DI para C++
1. Boost.DI
Boost.DI — uma das bibliotecas mais populares. Ela usa modelos C++14/17 para resolução automática de dependências. Suporta polimorfismo, escopos (singleton, unique) e configuração modular.
Exemplo de uso:
#include <boost/di.hpp>#include <iostream>
namespace di = boost::di;
class IWriter {public: virtual ~IWriter() = default; virtual void write(const std::string&) = 0;};
class ConsoleWriter : public IWriter {public: void write(const std::string& msg) override { std::cout << msg << std::endl; }};
class App { std::shared_ptr<IWriter> writer;public: explicit App(std::shared_ptr<IWriter> w) : writer(std::move(w)) {} void run() { writer->write("Hello from Boost.DI!"); }};
int main() { auto injector = di::make_injector( di::bind<IWriter>.to<ConsoleWriter>() ); auto app = injector.create<std::unique_ptr<App>>(); app->run(); return 0;}Prós: tipagem forte, overhead mínimo em tempo de execução, suporte a C++14/17.
Contras: mensagens de erro de compilação complexas, aumento do tempo de compilação.
2. Google Fruit
Google Fruit — contêiner do Google, funcionando em C++11. Usa macros para registrar componentes e suporta injeção via construtor.
Exemplo:
#include <fruit/fruit.h>#include <iostream>
class IWriter {public: virtual void write(const std::string&) = 0;};
class ConsoleWriter : public IWriter {publi Também na biblioteca
Trabalho com CSV em C++: biblioteca libcsv e ferramentas padrão
Cache em Node.js: Redis, Memcached e Node-Cache
Geolocalização e Mapas em PHP: Visão Geral de Bibliotecas e Ferramentas
Métricas Prometheus em Swift: monitoramento de aplicativos iOS/macOS