Multitudes construyendo una implementación. DI desesperada? ¿Usar el localizador de servicios?

13

Supongamos que tenemos 1001 clientes que construyen sus dependencias directamente en lugar de aceptar inyecciones. Refactorizar el 1001 no es una opción de acuerdo con nuestro jefe. En realidad, ni siquiera se nos permite acceder a su fuente, solo a los archivos de clase.

Lo que se supone que debemos hacer es "modernizar" el sistema por el que pasan estos 1001 clientes. Podemos refactorizar que a todos nos gusta. Las dependencias son parte de ese sistema. Y algunas de esas dependencias se supone que debemos cambiar para tener una nueva implementación.

Lo que nos gustaría hacer es tener la capacidad de configurar diferentes implementaciones de dependencias para satisfacer esta multitud de clientes. Lamentablemente, DI no parece ser una opción ya que los clientes no aceptan inyecciones con constructores o configuradores.

Las opciones:

1) Refactoriza la implementación del servicio que los clientes usan para que haga lo que los clientes necesitan ahora. Bang, hemos terminado. No es flexible. No es complejo.

2) Refactorice la implementación para que delegue su trabajo en otra dependencia que adquiera a través de una fábrica. Ahora podemos controlar qué implementación usan todos al refactorizar la fábrica.

3) Refactorice la implementación para que delegue su trabajo en otra dependencia que adquiera a través de un localizador de servicios. Ahora podemos controlar qué implementación utilizan todos configurando el localizador de servicios que podría ser simplemente un hashmap de cadenas a objetos con un poco de conversión en curso.

4) Algo en lo que ni siquiera he pensado todavía.

El objetivo:

Minimice el daño de diseño causado por arrastrar el viejo código de cliente mal diseñado hacia el futuro sin agregar complejidad inútil.

Los clientes no deben saber ni controlar la implementación de sus dependencias, pero insisten en construirlos con new . No podemos controlar el new pero sí controlamos la clase que están construyendo.

Mi pregunta:

¿Qué no he considerado?

Preguntas del Doc Brown

  

¿Realmente necesitas una posibilidad de configurar entre diferentes implementaciones? ¿Para qué propósito?

Agilidad. Muchas incógnitas. La gerencia quiere potencial para el cambio. Sólo perder la dependencia del mundo exterior. También pruebas.

  

¿necesita una mecánica de tiempo de ejecución, o simplemente una mecánica de tiempo de compilación para cambiar entre diferentes implementaciones? ¿Por qué?

Es probable que la mecánica de tiempo de compilación sea suficiente. A excepción de las pruebas.

  

¿Qué granularidad necesita para cambiar entre las implementaciones? ¿De repente? Por módulo (cada uno contiene un grupo de clases)? ¿Por clase?

De los 1001 solo uno se ejecuta a través del sistema a la vez. Cambiar lo que todos los clientes usan a la vez es probable que esté bien. Sin embargo, el control individual de las dependencias es importante.

  

¿Quién necesita controlar el interruptor? ¿Solo tu / tu equipo de desarrolladores? ¿Un administrador? ¿Cada cliente por su cuenta? ¿O los desarrolladores de mantenimiento para el código del cliente? Entonces, ¿qué tan fácil / robusto / infalible debe ser la mecánica?

Dev para pruebas. Administrador a medida que cambian las dependencias del hardware externo. Debe ser fácil de probar y configurar.

Nuestro objetivo es mostrar que el sistema se puede rehacer rápidamente y modernizar.

  

caso de uso real para el cambio de implementación?

Uno es que el software proporcionará algunos datos hasta que la solución de hardware esté lista.

    
pregunta candied_orange 02.02.2017 - 04:27

3 respuestas

6

Bueno, no estoy seguro de entender completamente los detalles técnicos y sus diferencias exactas de sus soluciones compatibles, pero en mi humilde opinión, primero debe averiguar qué tipo de flexibilidad realmente necesita.

Las preguntas que debes hacerte son:

  • ¿realmente necesita una posibilidad de configuración entre diferentes implementaciones? ¿Con qué propósito?

  • ¿necesita una mecánica de tiempo de ejecución, o simplemente una mecánica de tiempo de compilación para cambiar entre diferentes implementaciones? ¿Por qué?

  • ¿Qué granularidad necesita para cambiar entre las implementaciones? ¿De repente? Por módulo (cada uno contiene un grupo de clases)? ¿Por clase?

  • ¿Quién necesita controlar el interruptor? ¿Solo tu / tu equipo de desarrolladores? ¿Un administrador? ¿Cada cliente por su cuenta? ¿O los desarrolladores de mantenimiento para el código del cliente? Entonces, ¿qué tan fácil / robusto / infalible debe ser la mecánica?

Teniendo en mente las respuestas a estas preguntas, elija la solución más simple que se le ocurra y que le brinde la flexibilidad requerida. No implemente ninguna flexibilidad de la que no esté seguro "por si acaso" si eso significa un esfuerzo adicional.

Como respuesta a sus respuestas: si tiene al menos un caso de uso real , utilícelo para validar sus decisiones de diseño. Utilice ese caso de uso para averiguar qué tipo de solución funciona mejor para usted. Solo pruebe si una "fábrica" o un "localizador de servicios" le proporciona lo que necesita, o si necesita algo más. Si crees que ambas soluciones son igual de buenas para tu caso, lanza un dado.

    
respondido por el Doc Brown 02.02.2017 - 07:05
2

Solo para asegurarme de que estoy haciendo esto bien. Tienes algún servicio hecho de algunas clases, por ejemplo, C1,...,Cn y un grupo de clientes que llaman directamente a new Ci(...) . Así que creo que estoy de acuerdo con la estructura general de su solución, que consiste en crear un nuevo servicio interno con algunas clases nuevas D1,...,Dn que sean agradables y modernas e inyectar sus dependencias (permitiendo esas dependencias a través de la pila) y luego reescribir Ci para no hacer nada más que crear una instancia y delgate a Di . La pregunta es cómo hacerlo y sugirió un par de maneras en 2 y 3 .

Para dar una sugerencia similar a 2 . Puede usar la inyección de dependencia en Di e internamente y luego crear una raíz de composición normal R (utilizando un marco si lo considera apropiado) que sea responsable de ensamblar el gráfico de objetos. Empuje R detrás de una fábrica estática y deje que cada Ci obtenga Di a través de eso. Así, por ejemplo, podría tener

public class OldClassI {
    private final NewClassI newImplementation;

    public OldClassI(Object parameter) {
        this.newImplementation = CompositionRoot.getInstance().getNewClassI(parameter);
    }
}

Esencialmente, esta es su solución 2, pero reúne todas sus fábricas en un solo lugar junto con el resto de la inyección de dependencia.

    
respondido por el walpen 02.02.2017 - 06:26
1

Comience con implementaciones sencillas, sencillas y sencillas.

Si más adelante necesita crear implementaciones adicionales, se trata de cuándo se produce la decisión de implementación.

Si necesita flexibilidad de "tiempo de instalación" (cada instalación de cliente utiliza una implementación única y estática), aún no necesita hacer nada sofisticado. Simplemente ofrece diferentes DLL o SO (o cualquiera que sea su formato de biblioteca). El cliente solo necesita colocar el correcto en la carpeta lib ...

Si necesita flexibilidad en tiempo de ejecución, solo necesitará un adaptador delgado y un mecanismo de selección de implementación. Si utiliza una fábrica, un localizador o un contenedor de IoC, en su mayoría es discutible. Las únicas diferencias significativas entre un adaptador y un localizador son A) Nombrar y B) Si el objeto devuelto es un singleton o una instancia dedicada. Y la gran diferencia entre un contenedor de IoC y una fábrica / localizador es quién llama a quién . (A menudo es una cuestión de preferencia personal.)

    
respondido por el svidgen 02.02.2017 - 17:12