Recomendaciones sobre la integración del contenedor DI / IoC en una aplicación existente

10

Ahora me encuentro con la integración de un contenedor de inversión de control (IoC) en una aplicación existente, y estoy buscando algunas recomendaciones sobre cómo se puede lograr más fácilmente con el objetivo final de reducir el acoplamiento, lo que aumenta la capacidad de prueba. Aunque generalmente no clasificaría la mayoría de las clases como objetos divinos , cada una tiene demasiadas responsabilidades y dependencias ocultas a través de estadísticas, elementos únicos y falta de interfaces.

Aquí hay algunos antecedentes de algunos de los desafíos que deben enfrentarse:

  • La inyección de dependencia se usa con poca frecuencia
  • Abundan los métodos estáticos, tanto como métodos de fábrica como de ayuda.
  • Los singletons son bastante frecuentes
  • Las interfaces, cuando se usan, no son demasiado granulares
  • Los objetos a menudo atraen dependencias innecesarias a través de clases base

Nuestra intención es que la próxima vez que necesitemos hacer cambios en un área en particular, tratemos de descifrar las dependencias que, en realidad, existen pero que están ocultas detrás de los globales, como los singletons y las estadísticas.

Supongo que eso hace que el contenedor de IoC sea secundario a la introducción de la inyección de dependencias, pero esperaría que haya un conjunto de prácticas y recomendaciones que podrían seguirse o considerarse que nos ayudarán a romper estas dependencias.

Kaleb Pederson
fuente
3
Tengo que preguntar sobre la razón para hacer esto ahora ... ¿qué está impulsando este cambio? Mantenibilidad? ¿Escalabilidad ya que crecerá exponencialmente? Los desarrolladores están aburridos?
Aaron McIver
1
Recientemente me uní a la compañía y estoy tratando de presentar algunas "mejores prácticas". Mi objetivo principal es aumentar la capacidad de prueba y reducir el acoplamiento. Podemos usar fácilmente DI / IoC para el nuevo código y no tenemos la intención de intentar cambiar todo el código existente a la vez, pero me gustaría obtener recomendaciones sobre cómo podemos cambiar mejor el código existente para mejor la próxima vez que hagamos cambios en esa área. Hasta ahora, lo único que he encontrado en línea es lo siguiente: code.google.com/p/autofac/wiki/ExistingApplications
Kaleb Pederson el
3
A menos que exista una gran cantidad de pruebas de unidad / integración automatizadas; modificar la infraestructura existente a menos que exista un problema en aras de las mejores prácticas es pedir problemas. Incluso si su conjunto de pruebas de unidad / integración era sólido, todavía tenía dudas.
Aaron McIver
1
@ Aaron, espero que no parezca que lo estamos haciendo por las mejores prácticas. Estamos haciendo cambios porque es difícil y lento trabajar con el código existente y hacerlo poco a poco mientras trabajamos en esa área en particular. Con mucho gusto, tenemos un conjunto de pruebas de integración razonables y algunas pruebas unitarias para apoyar la realización de cambios.
Kaleb Pederson el

Respuestas:

8

Para lograr algo como esto, tendrá que trabajar en pasos, cada uno de estos no es trivial, pero se pueden lograr. Antes de comenzar, debe comprender que no puede apresurar este proceso.

  1. Defina los principales subsistemas de la aplicación y una interfacepara cada uno de ellos. Esta interfaz solo debe definir los métodos que otras partes del sistema usarán para hablar con él. NOTA: puede que tenga que tomar más de un pase en este momento.
  2. Proporcione una implementación envolvente de esa interfaz que delegue al código existente. El propósito de este ejercicio es evitar reescribir en masa , pero refactorizar el código para usar las nuevas interfaces, es decir, reducir el acoplamiento en su sistema.
  3. Configure el contenedor de IoC para construir el sistema utilizando las interfaces y las implementaciones que creó. En esta etapa, debe ocuparse de crear instancias del contenedor de IoC para que pueda abrir la aplicación. Es decir, si está en un entorno de servlet, asegúrese de que puede obtener / crear el contenedor en el init()método de servlet .
  4. Haga lo mismo dentro de cada subsistema nuevamente, esta vez cuando refactorice, está convirtiendo su implementación de código auxiliar en algo real que a su vez usa interfaces para comunicarse con sus componentes.
  5. Repita según sea necesario hasta que tenga un buen equilibrio entre el tamaño de los componentes y la funcionalidad.

Cuando haya terminado, los únicos métodos estáticos que debe tener en su sistema son los que realmente son funciones, por ejemplo, mire la Collectionsclase o la Mathclase. Ningún método estático debería intentar acceder directamente a otros componentes.

Esto es algo que llevará mucho tiempo, planifique en consecuencia. Asegúrese de que a medida que realiza su refactorización se vuelve más SÓLIDO en su enfoque de diseño. Al principio será doloroso. Estás cambiando drásticamente el diseño de tu aplicación de forma iterativa.

Berin Loritsch
fuente
Buenas sugerencias También remito a otras personas a las sugerencias aquí al hacer tales cambios: code.google.com/p/autofac/wiki/ExistingApplications
Kaleb Pederson
7

Elija Trabajar eficazmente con Legacy Code y siga sus consejos. Comience construyendo islas de código cubierto y avance gradualmente hacia una aplicación más bien estructurada. Intentar hacer el cambio en masa es una receta para el desastre.

Michael Brown
fuente
Me encanta ese libro!
Martijn Verburg
Buena recomendación Aunque leí la mitad hace años, imagino que sacaría mucho más provecho ahora que estoy en la situación en la cintura.
Kaleb Pederson el
Es curioso, la respuesta seleccionada básicamente resume el libro;) Por supuesto, Feathers entra en mucho más detalle.
Michael Brown
5

La razón principal para introducir IoC es el desacoplamiento de los módulos. El problema, especialmente con Java, es la extraordinaria unión fuerte que proporciona el newoperador, combinada con eso significa que el código de llamada sabe exactamente qué módulo utilizará.

Las fábricas se introdujeron para trasladar este conocimiento a un lugar central, pero al final todavía se conectan los módulos al usar new/ singleton que mantiene el enlace rígido, o se lee en un archivo de configuración y se usa la reflexión / Class.forName que es frágil cuando se refactoriza .

Si no tiene el objetivo modularizado, IoC no le dará nada.

La introducción de pruebas unitarias probablemente cambiará esto, ya que necesitará simular módulos que no están bajo prueba, y la forma más fácil de manejar esto, así como los módulos de producción reales con el mismo código, es usar un marco IoC para inyectar el apropiado módulos


fuente