Estoy refactorizando una clase y agregando una nueva dependencia. La clase actualmente está tomando sus dependencias existentes en el constructor. Entonces, por coherencia, agrego el parámetro al constructor.
Por supuesto, hay algunas subclases y aún más para las pruebas unitarias, así que ahora estoy jugando el juego de alterar a todos los constructores para que coincidan, y está tomando años.
Me hace pensar que usar propiedades con setters es una mejor manera de obtener dependencias. No creo que las dependencias inyectadas sean parte de la interfaz para construir una instancia de una clase. Agrega una dependencia y ahora todos sus usuarios (subclases y cualquiera que lo instale directamente) de repente lo sepan. Eso se siente como un descanso de encapsulación.
Este no parece ser el patrón con el código existente aquí, así que estoy buscando averiguar cuál es el consenso general, los pros y los contras de los constructores versus las propiedades. ¿Es mejor usar establecedores de propiedades?
fuente
Se supone que los usuarios de una clase deben conocer las dependencias de una clase determinada. Si tuviera una clase que, por ejemplo, se conectara a una base de datos, y no proporcionara un medio para inyectar una dependencia de la capa de persistencia, un usuario nunca sabría que una conexión a la base de datos tendría que estar disponible. Sin embargo, si modifico el constructor, les hago saber a los usuarios que hay una dependencia en la capa de persistencia.
Además, para evitar tener que modificar cada uso del viejo constructor, simplemente aplique el encadenamiento del constructor como un puente temporal entre el viejo y el nuevo constructor.
Uno de los puntos de inyección de dependencias es revelar qué dependencias tiene la clase. Si la clase tiene demasiadas dependencias, entonces puede ser hora de que tenga lugar alguna refactorización: ¿Todos los métodos de la clase utilizan todas las dependencias? Si no, entonces ese es un buen punto de partida para ver dónde podría dividirse la clase.
fuente
Por supuesto, poner el constructor significa que puede validar todo a la vez. Si asigna cosas en campos de solo lectura, tiene algunas garantías sobre las dependencias de su objeto desde el momento de la construcción.
Es un verdadero dolor agregar nuevas dependencias, pero al menos de esta manera el compilador sigue quejándose hasta que sea correcto. Lo cual es bueno, creo.
fuente
Si tiene una gran cantidad de dependencias opcionales (que ya es un olor), entonces probablemente la mejor opción sea la inyección de setter. Sin embargo, la inyección de constructor revela mejor sus dependencias.
fuente
El enfoque general preferido es utilizar la inyección del constructor tanto como sea posible.
La inyección de constructor establece exactamente cuáles son las dependencias requeridas para que el objeto funcione correctamente: nada es más molesto que renovar un objeto y hacer que se bloquee al llamar a un método porque no se establece alguna dependencia. El objeto devuelto por un constructor debe estar en estado de funcionamiento.
Intente tener un solo constructor, mantiene el diseño simple y evita la ambigüedad (si no es para los humanos, para el contenedor DI).
Puede usar la inyección de propiedad cuando tiene lo que Mark Seemann llama un valor predeterminado local en su libro "Inyección de dependencias en .NET": la dependencia es opcional porque puede proporcionar una implementación de trabajo fina pero desea permitir que la persona que llama especifique una diferente si necesario.
(Antigua respuesta a continuación)
Creo que la inyección del constructor es mejor si la inyección es obligatoria. Si esto agrega demasiados constructores, considere usar fábricas en lugar de constructores.
La inyección de setter es buena si la inyección es opcional, o si desea cambiarla hasta la mitad. Generalmente no me gustan los setters, pero es cuestión de gustos.
fuente
Es en gran medida una cuestión de gusto personal. Personalmente, tiendo a preferir la inyección de setter, porque creo que le da más flexibilidad en la forma en que puede sustituir implementaciones en tiempo de ejecución. Además, los constructores con muchos argumentos no están limpios en mi opinión, y los argumentos proporcionados en un constructor deberían limitarse a argumentos no opcionales.
Mientras la interfaz de clases (API) sea clara en lo que necesita para realizar su tarea, eres bueno.
fuente
Personalmente, prefiero el "patrón" Extraer y anular sobre las inyecciones de dependencias en el constructor, en gran parte por la razón descrita en su pregunta. Puede establecer las propiedades como
virtual
y luego anular la implementación en una clase comprobable derivada.fuente
Prefiero la inyección del constructor porque ayuda a "hacer cumplir" los requisitos de dependencia de una clase. Si está en el centro, un consumidor tiene que configurar los objetos para que la aplicación se compile. Si usa la inyección de setter, es posible que no sepan que tienen un problema hasta el tiempo de ejecución, y dependiendo del objeto, puede ser tarde en el tiempo de ejecución.
Todavía uso la inyección de setter de vez en cuando cuando el objeto inyectado puede necesitar un montón de trabajo en sí mismo, como la inicialización.
fuente
Prefiero la inyección del constructor, porque esto parece más lógico. Es como decir que mi clase requiere estas dependencias para hacer su trabajo. Si es una dependencia opcional, las propiedades parecen razonables.
También utilizo la inyección de propiedades para configurar cosas a las que el contenedor no tiene referencias, como una vista ASP.NET en un presentador creado usando el contenedor.
No creo que rompa la encapsulación. El funcionamiento interno debe permanecer interno y las dependencias se ocupan de una preocupación diferente.
fuente
Una opción que podría valer la pena considerar es componer dependencias múltiples complejas a partir de dependencias simples simples. Es decir, definir clases adicionales para dependencias compuestas. Esto facilita un poco la inyección del constructor WRT, menos parámetros por llamada, y al mismo tiempo mantiene la necesidad de suministrar todas las dependencias para instanciar.
Por supuesto, tiene más sentido si hay algún tipo de agrupación lógica de dependencias, por lo que el compuesto es más que un agregado arbitrario, y tiene más sentido si hay múltiples dependientes para una sola dependencia compuesta, pero el "patrón" del bloque de parámetros tiene ha existido durante mucho tiempo, y la mayoría de los que he visto han sido bastante arbitrarios.
Personalmente, sin embargo, soy más fanático de usar métodos / establecedores de propiedades para especificar dependencias, opciones, etc. Los nombres de las llamadas ayudan a describir lo que está sucediendo. Sin embargo, es una buena idea proporcionar ejemplos de fragmentos de "así es cómo configurarlo" y asegurarse de que la clase dependiente realice suficientes comprobaciones de errores. Es posible que desee utilizar un modelo de estado finito para la configuración.
fuente
Recientemente me encontré con una situación en la que tenía múltiples dependencias en una clase, pero solo una de las dependencias iba a cambiar necesariamente en cada implementación. Dado que el acceso a los datos y las dependencias de registro de errores probablemente solo se cambiarían con fines de prueba, agregué parámetros opcionales para esas dependencias y proporcioné implementaciones predeterminadas de esas dependencias en mi código de constructor. De esta manera, la clase mantiene su comportamiento predeterminado a menos que sea anulado por el consumidor de la clase.
El uso de parámetros opcionales solo se puede lograr en marcos que los admitan, como .NET 4 (tanto para C # como para VB.NET, aunque VB.NET siempre los ha tenido). Por supuesto, puede lograr una funcionalidad similar simplemente usando una propiedad que puede ser reasignada por el consumidor de su clase, pero no obtiene la ventaja de la inmutabilidad proporcionada al tener un objeto de interfaz privado asignado a un parámetro del constructor.
Dicho todo esto, si está introduciendo una nueva dependencia que debe proporcionar cada consumidor, tendrá que refactorizar su constructor y todo el código que consume su clase. Mis sugerencias anteriores realmente solo se aplican si tiene el lujo de poder proporcionar una implementación predeterminada para todo su código actual, pero aún brinda la capacidad de anular la implementación predeterminada si es necesario.
fuente
Esta es una publicación antigua, pero si se necesita en el futuro, tal vez sea útil:
https://github.com/omegamit6zeichen/prinject
Tuve una idea similar y se me ocurrió este marco. Probablemente esté lejos de estar completo, pero es una idea de un marco centrado en la inyección de propiedades
fuente
Depende de cómo quieras implementarlo. Prefiero la inyección del constructor donde siento que los valores que intervienen en la implementación no cambian a menudo. Por ejemplo: si la estrategia de empresa es ir con el servidor Oracle, configuraré mis valores de fuente de datos para un bean que logre conexiones a través de la inyección del constructor. De lo contrario, si mi aplicación es un producto y existe la posibilidad de que se pueda conectar a cualquier base de datos del cliente, implementaría dicha configuración de base de datos y la implementación de múltiples marcas a través de la inyección de setter. Acabo de tomar un ejemplo, pero hay mejores formas de implementar los escenarios que mencioné anteriormente.
fuente
La inyección del constructor revela explícitamente las dependencias, lo que hace que el código sea más legible y menos propenso a errores de tiempo de ejecución no controlados si se verifican los argumentos en el constructor, pero realmente se reduce a una opinión personal, y cuanto más use DI, más tienden a balancearse de un lado a otro según el proyecto. Personalmente, tengo problemas con los olores de código como constructores con una larga lista de argumentos, y creo que el consumidor de un objeto debe conocer las dependencias para poder usar el objeto de todos modos, por lo que esto justifica el uso de la inyección de propiedades. No me gusta la naturaleza implícita de la inyección de propiedades, pero la encuentro más elegante, lo que da como resultado un código más limpio. Pero, por otro lado, la inyección del constructor ofrece un mayor grado de encapsulación,
Elija la inyección por constructor o por propiedad sabiamente según su escenario específico. Y no sienta que tiene que usar DI solo porque parece necesario y evitará malos olores de diseño y código. A veces no vale la pena usar un patrón si el esfuerzo y la complejidad superan el beneficio. Mantenlo simple.
fuente