Estoy jugando con la inyección de dependencia, pero no estoy seguro de hacerlo bien. Especialmente, no estoy seguro de cuál debería ser la forma correcta de crear clases con dependencias inyectadas.
Digamos que tengo una clase A que crea la clase B. La clase B depende de la clase C y la clase C depende de la clase D. ¿Quién debería ser responsable de crear la clase D?
Podría ser de clase A. Sin embargo, en un sistema grande, la clase A podría terminar creando y ensamblando una gran cantidad de objetos.
Una clase de generador separada que creará D, C y B. A usará esta clase de generador.
Alguna otra opción.
Además, leí mucho sobre contenedores DI. Sin embargo, parece que no hay marcos principales para C ++. Además, si entiendo correctamente, la DI puede realizarse bien incluso sin contenedores. ¿Estoy en lo correcto?
fuente
Respuestas:
Estás saltando pasos. Considere un conjunto de convenciones optimizadas para acoplamiento flojo y seguridad de excepción. Las reglas son así:
R1: si A contiene una B, entonces el constructor de A recibe una B completamente construida (es decir, no "dependencias de construcción de B"). Del mismo modo, si la construcción de B requiere una C, recibirá una C, no las dependencias de C.
R2: si se requiere una cadena completa de objetos para construir un objeto, la construcción encadenada se extrae / automatiza dentro de una fábrica (función o clase).
Código (std :: mover llamadas omitidas por simplicidad):
En dicho sistema, "quién crea D" es irrelevante, porque cuando llama a make_b, necesita una C, no una D.
Codigo del cliente:
Aquí, D es creado por el código del cliente. Naturalmente, si este código se repite más de una vez, es libre de extraerlo en una función (ver R2 arriba):
Existe una tendencia natural a omitir la definición de make_b (ignorar R1 ) y escribir el código directamente así:
En este caso, tiene los siguientes problemas:
tienes un código monolítico; Si llega a una situación en el código del cliente donde necesita hacer una B a partir de una C existente, no puede usar make_b. Necesitará escribir una nueva fábrica, o la definición de make_b, y todo el código del cliente usando el antiguo make_b.
Su visión de las dependencias se confunde cuando mira la fuente: ahora, al mirar la fuente, puede pensar que necesita una instancia D, cuando de hecho puede necesitar una C.
Ejemplo:
struct A { B make_b(C c); }
aumentará en gran medida el acoplamiento: ahora A necesita conocer las definiciones de B y C (en lugar de solo C). También tiene restricciones sobre cualquier código de cliente que use A, B, C y D, impuesto en su proyecto porque omitió un paso en la definición de un método de fábrica ( R1 ).TLDR: En resumen, no pase la dependencia más externa a una fábrica, sino las más cercanas. Esto hace que su código sea robusto, fácilmente modificable y convierte la pregunta que planteó ("quién crea D") en una pregunta irrelevante para la implementación de make_b (porque make_b ya no recibe una D sino una dependencia más inmediata - C - y esto es inyectado como un parámetro de make_b).
fuente
Hay un marco DI para C ++ (todavía en desarrollo AFAIK): Boost.DI .
Hay algunos comentarios útiles sobre el marco en reddit.
fuente
Cada vez que aparece una pregunta como esa, indica una dependencia para inyectar . La idea es "delegar" la responsabilidad fuera del objeto que parece tener problemas para determinar cómo manejarlo.
Usando su ejemplo, dado que la clase A no parece poseer suficiente "conocimiento" para imaginar cómo crear D, invierte el control y lo expone como una dependencia necesaria para la clase A, al hacer que requiera una fábrica que sepa cómo crear instancias de clase D.
fuente