Saltar a contenido

Última actualización:2026-10-12

C++ Inyección de dependencias

Hace poco, un miembro del Planeta de Conocimiento me pidió que tratara la inyección de dependencias, así que escribí este artículo, que parte de un ejemplo práctico: se explican los principios y la implementación; el código completo de la versión abreviada está en el Planeta de Conocimiento. Vamos al lío.

Esquema:

  • Dependencia directa
  • Inyección de dependencias por interfaz
  • Inyección de dependencias por plantillas

La inyección de dependencias es un patrón de diseño muy usado; normalmente se emplean interfaces o plantillas para mejorar la testeabilidad y el mantenimiento del código. Veámoslo con un ejemplo.

Supongamos una clase de servicio de usuarios UserService que necesita comunicarse con un servidor remoto a través de un cliente HTTP; en este caso podríamos escribir las tres implementaciones siguientes.

1.Dependencia directa

UserService depende internamente de HttpClient directamente, por ejemplo:

class HttpClient {
 public:
  std::string Get(const std::string& url);
  void Post(const std::string& url, const std::string& data);
};

class UserService {
 private:
  HttpClient m_httpClient;
};

Dentro de UserService normalmente habrá métodos que operan sobre m_httpClient; este enfoque es simple y directo, pero UserService queda acoplado al HttpClient concreto, lo que dificulta sustituir el objeto dependiente y también hacer pruebas unitarias.

De ahí surgen los dos métodos siguientes.

2.Inyección de dependencias por interfaz

Nuestro objetivo es que las pruebas resulten cómodas, sin necesidad de crear una clase concreta: que el usuario pueda hacer mock de una clase para probar. Para ello podemos pensar en una interfaz: extraemos HttpClient y obtenemos:

struct IHttpClient {
  virtual std::string Get(const std::string& url) = 0;
  virtual void Post(const std::string& url, const std::string& data) = 0;
  virtual ~IHttpClient() = default;
};
class UserService {
 private:
  std::unique_ptr<IHttpClient> m_httpClient;
};

Así, a la hora de probar se puede hacer mock de un client y probar con él, por ejemplo:

class MockHttpClient : public IHttpClient {
public:
  MOCK_METHOD(std::string, Get, (const std::string& url), (override));
  MOCK_METHOD(void, Post, (const std::string& url, const std::string& data), (override));
};

auto mockHttpClient = std::make_unique<MockHttpClient>();
UserService userService(std::move(mockHttpClient));

Tras esta refactorización podemos conseguir:

  • Ocultar los detalles de implementación en el archivo .cc
  • Trabajar contra una interfaz abstracta
  • Inyectar las dependencias durante la construcción del objeto

3.Inyección de dependencias por plantillas

La inyección de dependencias por plantillas usa las técnicas de plantillas de C++ para pasar el objeto dependiente como parámetro de plantilla a UserService. Al instanciar la plantilla se especifica la implementación concreta del cliente HTTP. Este enfoque resuelve la dependencia en tiempo de compilación, sin llamadas a funciones virtuales, lo que mejora el rendimiento. Además, permite que la clase UserService se integre con flexibilidad con distintas implementaciones de clientes HTTP y facilita las pruebas unitarias.

template <typename THttpClient>
class UserService {
 private:
  THttpClient m_httpClient;
};

En este caso resulta sencillo de probar, por ejemplo:

UserService<MockHttpClient> userService;

Este enfoque tiene las siguientes ventajas:

  • Las dependencias se inyectan durante la instanciación de la plantilla
  • No hay llamadas virtuales (nótese que THttpClient no es una interfaz, por lo que las llamadas pueden resolverse en tiempo de compilación)

Esto es todo el contenido de esta sección; el código completo está en el Planeta de Conocimiento, ¡suscríbete para descargarlo!

Temas relacionados

Comentarios