Dependency Injection em C++: guia completo com exemplos

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

Introdução à Dependency Injection em C++



No desenvolvimento moderno em C++, um dos princípios-chave para criar código flexível e testável é a Dependency Injection (DI) — injeção de dependências. Este padrão de projeto permite reduzir o acoplamento entre os componentes do sistema, tornando o código mais modular, reutilizável e fácil de testar.



Neste artigo, vamos analisar o que é Dependency Injection em C++, quais problemas ela resolve, explorar diferentes maneiras de implementá-la na prática e fornecer exemplos de código que ajudarão você a aplicar essa abordagem em seus projetos.



O problema da dependência direta



Antes de falarmos sobre a solução, vamos observar um problema típico. Imagine que temos uma classe UserService que usa um Logger para registrar logs:



class Logger {public:    void log(const std::string& message) {        std::cout << "[LOG]: " << message << std::endl;    }};

class UserService {private: Logger logger; // Dependência rígidapublic: void createUser(const std::string& name) { logger.log("Creating user: " + name); // ... lógica de criação de usuário }};


Aqui, UserService cria sua própria instância de Logger. Isso gera vários problemas:

  • Dificuldade de teste: não é possível substituir o logger por um mock-object durante os testes unitários.
  • Alto acoplamento: alterar o construtor de Logger exigirá modificar todas as classes que o utilizam.
  • Violação do SRP (Single Responsibility Principle): a classe é responsável não apenas por sua lógica, mas também pela criação de dependências.


O que é Dependency Injection?



Dependency Injection é um padrão no qual um objeto recebe suas dependências externamente, em vez de criá-las por conta própria. A ideia principal é: "não peça, receba".



Em C++, existem três maneiras principais de injetar dependências:

  • Constructor Injection — através do construtor.
  • Setter Injection — através de métodos setter.
  • Interface Injection — através de uma interface.


1. Constructor Injection (Injeção via construtor)



Esta é a maneira mais comum e preferida. As dependências são passadas para o construtor da classe:



#include <memory>#include <iostream>

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

class ConsoleLogger : public ILogger {public: void log(const std::string& message) override { std::cout << "[CONSOLE]: " << message << std::endl; }};

class FileLogger : public ILogger {public: void log(const std::string& message) override { // Lógica de escrita em arquivo std::cout << "[FILE]: " << message << std::endl; }};

class UserService {private: std::unique_ptr<ILogger> logger_; // Dependência via interfacepublic: // Constructor Injection explicit UserService(std::unique_ptr<ILogger> logger) : logger_(std::move(logger)) {}

void createUser(const std::string& name) { logger_->log("Creating user: " + name); // ... lógica }};

int main() { auto consoleLogger = std::make_unique<ConsoleLogger>(); UserService service(std::move(consoleLogger)); service.createUser("Alice"); return 0;}


Vantagens:

  • A dependência é explicitamente visível na assinatura do construtor.
  • O objeto está sempre em um estado válido após a criação.
  • Fácil de testar — é possível subst

Blogs

Book Recommendations