Multitudes construyendo una implementación. DI sin esperanza? Usar el localizador de servicios?

14

Digamos que tenemos 1001 clientes que construyen sus dependencias directamente en lugar de aceptar inyecciones. Refactorizar el 1001 no es una opción según 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 todo lo que queramos. Las dependencias son parte de ese sistema. Y algunas de esas dependencias que 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 a esta multitud de clientes. Lamentablemente, DI no parece una opción ya que los clientes no aceptan inyecciones con constructores o setters.

Las opciones:

1) Refactorice la implementación del servicio que usan los clientes 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 a otra dependencia que adquiera a través de una fábrica. Ahora podemos controlar qué implementación utilizan todos refactorizando la fábrica.

3) Refactorice la implementación para que delegue su trabajo a 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 una hashmapcadena de objetos con una pequeña conversión.

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

El objetivo:

Minimice el daño de diseño causado por arrastrar el antiguo código de cliente mal diseñado al futuro sin agregar una complejidad sin sentido.

Los clientes no deben conocer o controlar la implementación de sus dependencias, pero insisten en construirlas new. No podemos controlar el, newpero controlamos la clase que están construyendo.

Mi pregunta:

¿Qué he dejado de considerar?


Preguntas de Doc Brown

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

Agilidad. Muchas incógnitas. La gerencia quiere potencial para el cambio. Solo pierde la dependencia del mundo exterior. También probando.

¿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é?

La mecánica del tiempo de compilación es bastante probable. Excepto por las pruebas.

¿Qué granularidad necesita para cambiar entre 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 en cualquier momento. Cambiar lo que todos los clientes usan a la vez probablemente esté bien. Sin embargo, el control individual de las dependencias es probablemente 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 / robusta / infalible necesita ser la mecánica?

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


Nuestro objetivo es mostrar que el sistema puede rehacerse rápidamente y modernizarse.


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

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

naranja confitada
fuente
1
No creo que logre mucho almacenando fábricas o localizadores en un estado global. ¿Por qué molestarse? ¿Vas a probar clientes sustituyendo la dependencia?
Basilevs
Considere usar un cargador de clases personalizado.
Basilevs
Solo por curiosidad, ¿qué hace este sistema?
Prime

Respuestas:

7

Bueno, no estoy seguro de comprender 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 debe hacerse son:

  • ¿realmente necesita una posibilidad de configurar 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 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 / robusta / infalible necesita 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 significa un esfuerzo adicional.

Como respuesta a sus respuestas: si tiene al menos un caso de uso real a la mano, úselo para validar sus decisiones de diseño. Use ese caso de uso para descubrir qué tipo de solución funciona mejor para usted. Simplemente 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 igualmente buenas para tu caso, tira un dado.

Doc Brown
fuente
¡Buena pregunta! Por favor vea la actualización.
candied_orange
Actualizado con caso de uso real.
candied_orange
@CandiedOrange: No puedo darte un poco de pescado, solo puedo tratar de ayudarte a pescar solo :-)
Doc Brown
Entendido. Solo estoy mirando el agua preguntándome si necesito un cebo mejor o un pozo de pesca diferente. Me encantaría hacer una DI adecuada, pero esta situación no parece permitirlo.
candied_orange
1
@CandiedOrange No se obsesione con el deseo de hacer DI porque es "bueno" o "mejor" que cualquier otra cosa. DI es una solución particular a un problema (o algunas veces varias). Pero, no siempre es la solución más "adecuada". No te enamores de él ... trátalo como es. Es una herramienta
svidgen
2

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

Para dar una sugerencia similar a 2. Puede usar la inyección de dependencia en todo Diy de forma interna y luego crear una raíz de composición normal R(usando un marco si lo considera apropiado) que es responsable de ensamblar el gráfico de objetos. Empújese Rdetrás de una fábrica estática y deje que cada uno Cilo Disupere. Entonces, por ejemplo, podrías 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 una ubicación junto con el resto de la inyección de dependencia.

Walpen
fuente
Estoy de acuerdo con ésto. Simplemente no estoy seguro de cómo esto no es lo mismo que el localizador de servicios de la opción 3. ¿Espera construir una nueva instancia con cada llamada agetInstance() ?
candied_orange
@CandiedOrange Esto es probablemente solo un choque en el uso, para mí, un localizador de servicios es muy específicamente algo que es esencialmente solo un hashmap de tipos a objetos, mientras que esta raíz de composición es solo un objeto con un montón de métodos que construyen otros objetos.
walpen
1

Comience con implementaciones simples, nada sofisticadas y singulares.

Si luego necesita crear implementaciones adicionales, es una cuestión de cuándo se produce la decisión de implementación.

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

Si necesita flexibilidad en el tiempo de ejecución, solo necesitará un adaptador delgado y un mecanismo de selección de implementación. Ya sea que use una fábrica, un localizador o un contenedor de IoC, es principalmente discutible. Lo único diferencias significativas entre un adaptador y un localizador son A) Naming y B) Si el objeto devuelto es un singleton o una instancia dedicada. Y la gran diferencia entre un contenedor IoC y una fábrica / localizador es quién llama a quién . (A menudo es una cuestión de preferencia personal).

svidgen
fuente