A nivel de clase, es fácil.
La 'Inyección de dependencia' simplemente responde la pregunta "¿cómo voy a encontrar a mis colaboradores" con "te presionan? No tienes que ir a buscarlos tú mismo". (Es similar a 'Inversión de control', pero no lo mismo, donde la pregunta "¿cómo ordenaré mis operaciones sobre mis entradas?" Tiene una respuesta similar).
El único beneficio de que sus colaboradores lo presionen es que permite que el código del cliente use su clase para componer un gráfico de objetos que se adapte a sus necesidades actuales ... no ha predeterminado arbitrariamente la forma y la mutabilidad del gráfico al decidir en privado Los tipos concretos y el ciclo de vida de sus colaboradores.
(Todos esos otros beneficios, de comprobabilidad, de acoplamiento flojo, etc., se derivan en gran medida del uso de interfaces y no tanto de la dependencia-inyección-ness, aunque DI promueve naturalmente el uso de interfaces).
Vale la pena señalar que, si no evitar crear instancias de sus propios colaboradores, su clase debe , por tanto, obtener sus colaboradores de un constructor, una propiedad o un método de argumento (esta última opción se suele pasar por alto, por cierto ... lo hace no siempre tiene sentido que los colaboradores de una clase formen parte de su "estado").
Y eso es algo bueno.
A nivel de aplicación ...
Esto en cuanto a la visión de las clases por clase. Supongamos que tiene un montón de clases que siguen la regla de "no crear instancias de sus propios colaboradores" y desea hacer una aplicación a partir de ellas. Lo más simple es usar un buen código antiguo (¡una herramienta realmente útil para invocar constructores, propiedades y métodos!) Para componer el gráfico de objeto que desee y arrojar algo de entrada. (Sí, algunos de esos objetos en su gráfico serán fábricas de objetos, que se han pasado como colaboradores a otros objetos de larga vida en el gráfico, listos para el servicio ... ¡no puede preconstruir cada objeto! )
... su necesidad de configurar 'flexiblemente' el gráfico de objetos de su aplicación ...
Dependiendo de sus otros objetivos (no relacionados con el código), es posible que desee otorgarle al usuario final cierto control sobre el gráfico de objeto implementado. Esto lo lleva en la dirección de un esquema de configuración, de un tipo u otro, ya sea un archivo de texto de su propio diseño con algunos pares de nombre / valor, un archivo XML, un DSL personalizado, un lenguaje declarativo de descripción gráfica como YAML, un lenguaje de secuencias de comandos imperativo como JavaScript, o algo más apropiado para la tarea en cuestión. Lo que sea necesario para componer un gráfico de objeto válido, de una manera que satisfaga las necesidades de sus usuarios.
... puede ser una fuerza de diseño significativa.
En el más extremo circunstancia de ese tipo, se puede optar por tomar un enfoque muy general, y dar a los usuarios finales un general de mecanismo de 'cableado' el objeto gráfico de su elección e incluso permitir que ellos para proporcionar realizaciones concretas de interfaces para la tiempo de ejecución! (Su documentación es una joya reluciente, sus usuarios son muy inteligentes, están familiarizados con al menos el contorno aproximado del gráfico de objetos de su aplicación, pero no tienen un compilador a mano). Este escenario puede ocurrir teóricamente en algunas situaciones de 'empresa'.
En ese caso, es probable que tenga un lenguaje declarativo que permita a sus usuarios expresar cualquier tipo, la composición de un gráfico de objetos de esos tipos y una paleta de interfaces que el usuario final mítico puede mezclar y combinar. Para reducir la carga cognitiva en sus usuarios, prefiere un enfoque de 'configuración por convención', de modo que solo tengan que intervenir y anular el fragmento de gráfico de objeto de interés, en lugar de luchar con todo.
¡Pobre diablo!
Debido a que no le gustaba escribir todo eso usted mismo (pero en serio, consulte un enlace de YAML para su idioma), está utilizando un marco DI de algún tipo.
Dependiendo de la madurez de ese marco, es posible que no tenga la opción de usar la inyección de constructor, incluso cuando tenga sentido (los colaboradores no cambian durante la vida útil de un objeto), lo que le obliga a usar Setter Injection (incluso cuando los colaboradores no cambian durante la vida útil de un objeto, e incluso cuando no hay realmente una razón lógica por la cual todas las implementaciones concretas de una interfaz deben tener colaboradores de un tipo específico). Si es así, actualmente estás en un infierno de acoplamiento fuerte, a pesar de tener diligentemente 'interfaces utilizadas' en toda tu base de código: ¡horror!
Sin embargo, con suerte, usó un marco DI que le da la opción de inyección de constructor, y sus usuarios solo están un poco malhumorados con usted por no pasar más tiempo pensando en las cosas específicas que necesitaban para configurar y dándoles una interfaz de usuario más adecuada para la tarea a mano. (Aunque para ser justos, probablemente intentaste pensar en una forma, pero JavaEE te decepcionó y tuviste que recurrir a este horrible truco).
Bootnote
En ningún momento está utilizando Google Guice, que le brinda al codificador una forma de prescindir de la tarea de componer un gráfico de objeto con código ... escribiendo código. Argh!