inversion of control - register - Argumentos contra la inversión de contenedores de control
unity ioc (3)
Parece que todo el mundo se está moviendo hacia contenedores IoC. He intentado "asimilarlo" por un tiempo, y por más que no quiera ser el único conductor que vaya por el camino equivocado en la carretera, todavía no me pasa la prueba del sentido común. Déjeme explicarlo y, por favor, corríjame / escúcheme si mis argumentos son erróneos:
Según tengo entendido: se supone que los contenedores IoC le facilitarán la vida al combinar diferentes componentes. Esto se realiza a través de a) inyección del constructor, b) inyección del fijador yc) inyección de interfaz. Estos se "conectan" programáticamente o en un archivo que el contenedor lee. Luego, los componentes se convocan por nombre y luego se convierten manualmente cuando sea necesario.
Lo que no entiendo
EDIT : (Mejor fraseo) Por qué usar un contenedor opaco que no es idiomático para el idioma, cuando puede "cablear" la aplicación en (imho) de una manera mucho más clara si los componentes se diseñaron correctamente (utilizando patrones IoC, acoplamiento suelto) ? ¿Cómo este "código administrado" gana funcionalidad no trivial? (He escuchado algunas menciones a la administración del ciclo de vida, pero no necesariamente entiendo cómo esto es mejor / más rápido que hacerlo tú mismo).
ORIGINAL : ¿Por qué ir a todas las longitudes de almacenamiento de los componentes en un contenedor, "cablearlos" en formas que no son idiomáticas para el idioma, usar cosas equivalentes a "ir a las etiquetas" cuando llama a los componentes por su nombre y luego perder muchos de los beneficios de seguridad de un lenguaje de tipo estático mediante la conversión manual, cuando obtendría la funcionalidad equivalente al no hacerlo, y en su lugar, utilizar todas las características geniales de abstracción que ofrecen los lenguajes OO modernos, por ejemplo, ¿programar para una interfaz? Quiero decir, las partes que realmente necesitan usar el componente a mano tienen que saber que lo están usando en cualquier caso, y aquí estaría haciendo el "cableado" de la manera más natural e idiomática: ¡la programación!
Ciertamente hay personas que piensan que los Contenedores DI no agregan ningún beneficio , y la pregunta es válida. Si lo mira puramente desde un ángulo de composición de objeto, el beneficio de un contenedor puede parecer insignificante. Cualquier tercero puede conectar componentes acoplados libremente.
Sin embargo, una vez que vaya más allá de los escenarios de juguetes, debe darse cuenta de que el tercero que conecta a los colaboradores debe asumir más que la simple responsabilidad de la composición. También puede haber problemas de desmantelamiento para evitar fugas de recursos. Como el compositor es la única parte que sabe si una instancia dada fue compartida o privada, también debe asumir el rol de hacer la administración de por vida .
Cuando comienza a combinar varios ámbitos de instancia, utilizando una combinación de servicios compartidos y privados, y tal vez incluso de ámbito de algunos servicios a un contexto particular (como una solicitud web), las cosas se vuelven complejas. Ciertamente, es posible escribir todo ese código con la DI del hombre pobre, pero no agrega ningún valor comercial, es pura infraestructura.
Dicho código de infraestructura constituye un Subdominio genérico , por lo que es muy natural crear una biblioteca reutilizable para abordar tales preocupaciones. Eso es exactamente lo que es un contenedor DI.
Por cierto, la mayoría de los contenedores que conozco no usan nombres para conectarse, sino que utilizan Auto-wiring , que combina la información estática de Constructor Injection con la configuración del contenedor de asignaciones de interfaces a clases concretas. En resumen, los contenedores entienden esos patrones de forma nativa.
No se requiere un contenedor DI para DI, es muy útil.
Se puede encontrar un tratamiento más detallado en el artículo Cuándo usar un contenedor DI .
En primer lugar, ¿qué es el COI? Significa que la responsabilidad de crear el objeto dependiente se retira del objeto principal y se delega a un marco de terceros. Siempre uso la primavera como marco de trabajo de mi COI y aporta toneladas de beneficios a la mesa.
Promueve la codificación para la interfaz y el desacoplamiento : el beneficio clave es que la COI promueve y facilita el desacoplamiento. Siempre puede inyectar una interfaz en su objeto principal y luego usar los métodos de la interfaz para realizar tareas. El objeto principal no necesita saber qué objeto dependiente está asignado a la interfaz. Cuando quiera usar una clase diferente como dependencia, todo lo que necesita es intercambiar la clase anterior por una nueva en el archivo de configuración sin una sola línea de cambio de código. Ahora puede argumentar que esto se puede hacer en el código usando varios patrones de diseño de interfaz. Pero el marco de IOC hace su caminata en un parque. De modo que, incluso como novato, se convierte en un experto en el aprovechamiento de diversos patrones de diseño de interfaces como bridge, factory, etc.
Código limpio : como la mayoría de las operaciones de creación de objetos y de ciclo de vida de objetos se delegan al contenedor IOC que guardó desde el código repetitivo del punto de asador de escritura. Así que tienes un código más limpio, más pequeño y más comprensible.
Pruebas unitarias : IOC facilita las pruebas unitarias. Dado que se queda con el código desacoplado, puede probar fácilmente el código desacoplado de forma aislada. También puede inyectar fácilmente dependencias en sus casos de prueba y ver cómo interactúan los diferentes componentes.
Configuradores de propiedades : casi todas las aplicaciones tienen algún archivo de propiedades donde almacenan propiedades estáticas específicas de la aplicación. Ahora para acceder a esas propiedades, los desarrolladores necesitan escribir envoltorios que leerán y analizarán el archivo de propiedades y almacenarán las propiedades en el formato al que puede acceder la aplicación. Ahora todos los marcos de IOC proporcionan una forma de inyectar propiedades / valores estáticos en una clase específica. Así que esto de nuevo se convierte en paseo por el parque.
Estos son algunos de los puntos que puedo pensar de inmediato. Estoy seguro de que hay más.
Estoy seguro de que hay mucho que decir sobre el tema, y espero que edite esta respuesta para agregar más más tarde (y espero que más personas agreguen más respuestas y puntos de vista), pero solo un par de puntos rápidos en tu publicación ... .
El uso de un contenedor IoC es un subconjunto de inversión de control , no todo. Puede usar la inversión de control como una construcción de diseño sin depender de un marco contenedor IoC. En su forma más simple, la inversión de control se puede establecer en este contexto como "suministro, no instanciar". Mientras sus objetos no sean internos, según las implementaciones de otros objetos, y en su lugar requieren que se les proporcionen implementaciones creadas, entonces usted está usando la inversión de control. Incluso si no está utilizando un marco contenedor IoC.
Para su programación sobre una interfaz ... No estoy seguro de cuál ha sido su experiencia con los contenedores IoC (mi favorito personal es StructureMap ), pero definitivamente programa a una interfaz con IoC. La idea general, al menos en cómo la he usado, es que separa sus interfaces (sus tipos) de sus implementaciones (sus clases inyectadas). El código que se basa en las interfaces está programado solo para esos, y las implementaciones de esas interfaces se inyectan cuando es necesario.
Por ejemplo, puede tener un IFooRepository
que devuelve desde un almacén de datos instancias del tipo Foo
. Todo el código que necesita esas instancias los obtiene de un objeto suministrado de tipo IFooRepository
. En otros lugares, creas una implementación de FooRepository
y configuras tu IoC para proporcionar que en cualquier IFooRepository
se necesita un IFooRepository
. Esta implementación puede obtenerlos de una base de datos, de un archivo XML, de un servicio externo, etc. No importa dónde. Ese control ha sido invertido. A tu código que usa objetos de tipo Foo
no le importa de dónde vienen.
El beneficio obvio es que puede intercambiar esa implementación en cualquier momento que desee. Puede reemplazarlo con una versión de prueba, cambiar versiones según el entorno, etc. Pero tenga en cuenta que tampoco es necesario que tenga una proporción de interfaces 1 a 1 de este tipo en cualquier momento.
Por ejemplo, una vez usé una herramienta de generación de código en un trabajo anterior que escupía toneladas y toneladas de código DAL en una sola clase. Romperlo hubiera sido un dolor, pero lo que no era tanto un dolor fue configurarlo para que lo escupiera todo en nombres de propiedades / métodos específicos. Así que escribí un montón de interfaces para mis repositorios y generé esta clase que implementó todas ellas. Para esa clase generada, fue feo. Pero al resto de mi aplicación no le importó porque veía cada interfaz como su propio tipo. El contenedor IoC acaba de proporcionar la misma clase para cada uno.
Pudimos ponernos en marcha rápidamente con esto y nadie estaba esperando en el desarrollo de DAL. Mientras continuamos trabajando en el código de dominio que usaba las interfaces, a un desarrollador junior se le asignó la tarea de crear mejores implementaciones. Esas implementaciones se intercambiaron más tarde, todo fue bien.
Como mencioné anteriormente, todo esto se puede lograr sin un marco de contenedor IoC. Es el patrón en sí lo que realmente importa.