2011-10-03 18 views
7

Una pregunta sobre Guice. Todavía lo estoy aprendiendo, pero puedo entender los fundamentos.Guice runtime dependency parameters reinjection

Esta pregunta ya fue formulada un par de veces en la red, pero nunca con una respuesta concreta (ninguna que pude encontrar).

Digamos que tengo una situación similar a la de la imagen (un ejemplo similar fue algún que otro en la red).

enter image description here

public class Dog {} 

public class Walk implements Walkable { 
    private final Dog dog; 
    private final boolean leash; 

    @Inject 
    public Walk(Dog dog, @Assisted boolean leash) { 
     this.dog = dog; 
     this.leash = leash; 
    } 

    public void go() { 
    } 
} 

public interface Walkable { 
    void go(); 
} 

public interface WalkFactory { 
    Walk create(boolean leash); 
} 

public class AssistedMain { 
    public static void main(String[] args) { 
     Injector i = Guice.createInjector(new AbstractModule() { 
      protected void configure() { 

       install(new FactoryModuleBuilder(). 
         implement(Walkable.class, Walk.class). 
         build(WalkFactory.class)); 
      } 
     }); 

     Walk walk = i.getInstance(WalkFactory.class).create(true); 
    } 
} 

Eso es todo muy bien. Pero la pregunta es: ¿puedo, de alguna manera, reinyectar esa instancia de objeto al "contenedor" (inyector) para usar en las clases que dependen de esta dependencia?

Entonces, permite agregar un interface Person, class PersonImpl.

enter image description here

La nueva fuente de clases son:

public interface Person { 
    void walkDog(); 
} 

public class PersonImpl implements Person { 
    private Walkable walkable; 

    @Inject 
    public PersonImpl(Walkable walkable) { 
     this.walkable = walkable; 
    } 

    public void setWalkable(Walkable walkable) { 
     this.walkable = walkable; 
    } 

    public void walkDog() { 
     walkable.go(); 
    } 
} 

Entonces, la pregunta es - soy yo, de alguna manera capaz de inyectar en realidad este caso particular en el objeto añadido. Este es un ejemplo simple, pero podemos suponer que hay 10 niveles de clases debajo de este.

La solución que encontré no es muy flexible. Algo así como:

Injector i = Guice.createInjector(new SimpleModule(false, dog));

Y luego unirse a instancia concreta. Eso no es muy dinámico. Básicamente, cada vez que necesito un tiempo de ejecución/parámetro dinámico diferente, tengo que volver a crear el inyector.

El Provider<T> es bueno, el FactoryModuleBuilder ayuda, pero ¿cómo puedo inyectar los objetos?

¿Hay soluciones más dinámicas a este problema?

Gracias.

Respuesta

4

MPierce - agreed. Trataré de explicar la forma en que visualicé el problema (puedes corregirme si estoy equivocado).

Originalmente derivado de un patrón de "localizador de servicios", la idea de que puede administrar más que servicios es optimista por decir lo menos.

Podríamos dividir la aplicación en de datos y servicios clases, o se podría decir que tenemos aplicación y código de infraestructura - "inyección de dependencias", un gran libro.

Así que, básicamente, la inyección de dependencias y los marcos de inyección de dependencia en general son geniales. Para resolver la infraestructura o el código de "servicio".

Cualquier parámetro dinámico (tiempo de ejecución) que se inyecte en el contenedor/inyector es básicamente lo que le obliga a finalizar el gráfico de objetos.

Por ejemplo, tenemos el diseño folowing:

enter image description here

EmailMessage es un parámetro de tiempo de ejecución. Se puede "inyectar" en un servicio de correo electrónico fuera del contenedor/inyector, pero finaliza el gráfico del objeto. Si deseamos solicitar EmailDispatcher, después de que inyectamos EmailMessage en EmailService (que es, repito, hecho fuera del inyector), ya no podremos obtener EmailDispatcher del inyector.

Luego, podría rediseñar su modelo para que "encaje" en el concepto de parámetros dinámicos del Contenedor/Inyector.

enter image description here

Pero, de nuevo, que obligó al diseño, y de repente, EmailDispatcher tiene demasiadas responsabilidades. Podría usarse en un contexto así, donde no tenga muchas clases de infraestructura.

enter image description here

y cuando se tiene un diseño que tienes en el tercer ejemplo de la imagen, no se puede utilizar el inyector/contenedor para hacer salir una instancia NextService3 (ni por debajo del nivel de EmailDispatcher).

El problema es - si tiene algún parámetro dinámico (runtime), solo puede usar la inyección de dependencia para las clases que superen la clase que requiere un parámetro dinámico, puede olvidar las clases siguientes.

Phew.

¿Correcto?

+0

tiene sentido para mí. – mpierce

1

Parte del problema depende de cómo se resuelva que 'falso' es lo que desea establecer para el campo de correa. ¿Eso proviene de datos de configuración o qué?

Un método proveedor puede ser útil ...

class FooModule extends AbstractModule { 
... 
    @Provides 
    Walkable getWalkable(Dog dog) { 
     boolean leash = getBooleanFromSomewhere(); 
     return new Walk(dog, leash); 
    } 
} 

Si usted puede aclarar dónde booleano que está viniendo, me ayudará a entender qué tipo de enfoque es aplicable.

+0

La correa es un parámetro dinámico que alguna vez estuvo en la aplicación. Digamos que este parámetro es proporcionado por el usuario alguna vez en la aplicación (hay una casilla de verificación de la correa :)). El usuario puede marcar y desmarcar esa casilla y esperar que una persona pasee al perro con o sin la correa. El "problema" es que ese parámetro es dinámico y requeriría "reinyección" en el "contenedor", por lo que el gráfico del objeto "infraestructura" puede instanciar los objetos de acuerdo con ese parámetro. Este es un ejemplo simple, digamos que tiene 30 usuarios, y desea adquirir UserTransaction para cada uno, dependiendo del inicio de sesión. – pfh

+0

Ya veo. Tal vez la inyección no es la herramienta adecuada para esto, entonces. La inyección es a menudo incómoda cuando los parámetros que definen la inyección son controlados por los datos del usuario. (Argh, no puedo poner una nueva línea en un comentario?) Podría intentar usar una inyección de ámbito de aplicación donde las cosas que se inyectan dependen de los parámetros que usted determine inspeccionando al usuario autenticado, o refactorizando su API un poco para permitir 'leash' a se proporcionará como un parámetro de método en lugar de un campo en sus objetos. No es realmente la respuesta que está buscando, me doy cuenta, pero creo que está estirando los límites de lo que IoC hace bien. – mpierce

1

Puede usar custom scopes, al igual que cuando usa servlets de guice. De esta forma, puede crear su instancia y luego sembrar en el inyector.

+0

Una buena idea, pero realmente no resuelve el problema. El problema es la reinyección dinámica, en el "Enlace de la anotación a la implementación", el ejemplo instancia la instancia desde dentro del inyector. La misma funcionalidad podría lograrse pasando un objeto a guice antes de que se inicialice (eso sería aún más dinámico). El punto es que los ámbitos personalizados son útiles, pero no en este contexto: todavía tengo una instanciación en el inyector, lo que significa que es un contenedor estáticamente inicializado, sin darme realmente flexibilidad sobre la inyección del objeto. – pfh