Esto es similar a la respuesta votada, pero quiero pensar en voz alta, tal vez otros también vean las cosas de esta manera.
OO clásico utiliza constructores para definir el contrato público de "inicialización" para los consumidores de la clase (ocultando TODOS los detalles de implementación; también conocido como encapsulación). Este contrato puede garantizar que, después de la creación de instancias, tenga un objeto listo para usar (es decir, ningún paso de inicialización adicional para ser recordado (er, olvidado) por el usuario).
(constructor) DI sin lugar a dudas rompe la encapsulación al sangrar los detalles de implementación a través de esta interfaz de constructor público. Mientras sigamos considerando al constructor público responsable de definir el contrato de inicialización para los usuarios, hemos creado una violación horrible de la encapsulación.
Ejemplo teórico:
Class Foo tiene 4 métodos y necesita un número entero para la inicialización, por lo que su constructor se parece a Foo (int size) y los usuarios de la clase Foo tienen claro de inmediato que deben proporcionar un tamaño en la instanciación para que Foo funcione.
Digamos que esta implementación particular de Foo también puede necesitar un IWidget para hacer su trabajo. La inyección del constructor de esta dependencia nos haría crear un constructor como Foo (int size, widget de IWidget)
Lo que me molesta de esto es que ahora tenemos un constructor que combina datos de inicialización con dependencias: una entrada es de interés para el usuario de la clase ( tamaño ), la otra es una dependencia interna que solo sirve para confundir al usuario y es una implementación detalle ( widget ).
El parámetro de tamaño NO es una dependencia, es simple un valor de inicialización por instancia. IoC es excelente para las dependencias externas (como el widget) pero no para la inicialización del estado interno.
Peor aún, ¿qué pasa si el Widget solo es necesario para 2 de los 4 métodos en esta clase; ¡Puedo estar incurriendo en una sobrecarga de instanciación para Widget aunque no se pueda usar!
¿Cómo comprometer / conciliar esto?
Un enfoque es cambiar exclusivamente a interfaces para definir el contrato de operación; y abolir el uso de constructores por parte de los usuarios. Para ser coherente, todos los objetos tendrían que ser accedidos solo a través de interfaces, y instanciados solo a través de alguna forma de resolutor (como un contenedor IOC / DI). Solo el contenedor puede instanciar cosas.
Eso se encarga de la dependencia del widget, pero ¿cómo inicializamos el "tamaño" sin recurrir a un método de inicialización separado en la interfaz de Foo? Con esta solución, perdimos la capacidad de garantizar que una instancia de Foo esté completamente inicializada en el momento en que la obtenga. Qué fastidio, porque realmente me gusta la idea y la simplicidad de la inyección del constructor.
¿Cómo puedo lograr una inicialización garantizada en este mundo DI, cuando la inicialización es MÁS que SOLO dependencias externas?