Container de Injeção de Dependência em C++: biblioteca Fruit para injeção de dependências

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Container de Injeção de Dependência para C++: visão geral da biblioteca Fruit



No desenvolvimento moderno em C++, o gerenciamento de dependências entre classes torna-se criticamente importante para criar aplicações flexíveis, testáveis e de fácil manutenção. O padrão Dependency Injection (DI) permite externalizar a criação de objetos para fora das classes, e o container DI automatiza esse processo. Uma das melhores bibliotecas para implementar injeção de dependências em C++ é o Fruit — um container leve, rápido e type-safe que opera em tempo de compilação.



O que é um container de Injeção de Dependência?

Um container de Injeção de Dependência é uma ferramenta que assume a responsabilidade de criar e vincular objetos (dependências). Em vez de a própria classe criar suas dependências via new ou fábricas, o container as "injeta" externamente. Isso implementa o princípio Inversion of Control (IoC) e torna o código fracamente acoplado.



Por que Fruit?

A biblioteca Fruit (https://github.com/google/fruit) foi desenvolvida no Google e se diferencia de alternativas (como Boost.DI) pelas seguintes características:

  • Verificação em tempo de compilação: todas as ligações são verificadas em tempo de compilação, eliminando erros de execução relacionados à falta de dependências.
  • Overhead mínimo: Fruit não usa RTTI nem chamadas virtuais, gerando código eficiente.
  • Simplicidade: requer apenas C++14 e arquivos de cabeçalho — sem macros ou geração de código.
  • Suporte a componentes: é possível montar o grafo de dependências a partir de módulos reutilizáveis.


Instalação

Fruit é distribuído como uma biblioteca header-only (requer apenas fruit.h). Para instalação via vcpkg:

vcpkg install fruit

Ou clone o repositório e inclua o arquivo include/fruit/fruit.h.



Conceitos principais

No Fruit, são utilizados três elementos-chave:

  • Injetor (Injector) — o container que cria os objetos.
  • Componente (Component) — módulo que registra os tipos e suas fábricas.
  • Anotações — para diferenciar diferentes implementações de uma mesma interface.


Exemplo de uso

Vamos considerar um exemplo clássico: um serviço depende de um repositório. Criaremos uma interface e duas implementações:



// 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;};


Implementações:

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

class ConsoleLogger : public ILogger {public: INJECT(ConsoleLogger()) = default; // Anotação Fruit para o construtor 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); }};


Serviço que utiliza ambas as dependências:

// 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); }};


Agora vamos montar o componente

Recommendations