2011-07-08 9 views
6

Tengo este caso de uso que es muy similar al ejemplo de Robot-piernas de Guice, excepto que no sé cuántas "piernas" tengo. Por lo tanto, no puedo usar las anotaciones necesarias para el ejemplo de las piernas del robot.Generalizar el ejemplo de robot-legs de guice con Multibinding

Espero reunir todas estas "patas" en un java.util.Conjunto con la extensión Multibindings de Guice.

Técnicamente, en un PrivateModule me gustaría exponer una implementación directamente como un elemento del conjunto que proporcionará la extensión Multibindings. Simplemente no sé cómo hacer eso.

Para referencia y ejemplo de código, consulte el ejemplo de robot-piernas aquí: http://code.google.com/p/google-guice/wiki/FrequentlyAskedQuestions#How_do_I_build_two_similar_but_slightly_different_trees_of_objec


Aquí es mi caso de uso preciso:

Tengo el siguiente:

// Main application 
public interface MyTree {...} 
public interface MyInterface { 
    public MyTree getMyTree() {} 
} 
public abstract class MyModule extends PrivateModule {} 
public class MyManager { 
    @Inject MyManager (Set<MyInterface> interfaces){ this.interfaces = interfaces } 
} 
public class MainModule extends AbstractModule { 
    public void configure() { 
    // Install all MyModules using java.util.ServiceLoader. 
    } 
} 


// In expansion "square.jar" 
public class SquareTree implements MyTree {...} 
public class SquareImplementation implements MyInterface { 
    @Inject SquareImplementation (MyTree tree) { this.tree = tree; } 
    public MyTree getMyTree() { return this.tree; } 
} 
public class SquareModule extends MyModule { // correctly defined as a ServiceLoader's service. 
    public void configure() { 
    // How to make this public IN a multibinder's set? 
    bind(MyInterface.class).to(SquareImplementation.class); 

    // Implementation specific to the Squareimplementation. 
    bind(MyTree.class).to(SquareTree.class); 
    } 
} 

// In expansion "circle.jar" 
public class CircleTree implements MyTree {...} 
public class CircleImplementation implements MyInterface { 
    @Inject CircleImplementation (MyTree tree) { this.tree = tree; } 
    public MyTree getMyTree() { return this.tree; } 
} 
public class CircleModule extends MyModule { // correctly defined as a ServiceLoader's service. 
    public void configure() { 
    // How to make this public IN a multibinder's set? 
    bind(MyInterface.class).to(CircleImplementation.class); 

    // Implementation specific to the Circle implementation. 
    bind(MyTree.class).to(CircleTree.class); 
    } 
} 

Desde Estoy hablando de tarros de expansión, no los conozco al principio, ni siquiera sé cuántos de ellos existen: necesito cargar el MyModule s con j.u.ServiceLoader y cada módulo debe definir una implementación MyInterface (estas dos partes son correctas).

El problema es obtener todas las implementaciones MyInterface en un conjunto (en MyManager). ¿Cómo puedo hacer eso?


solución, completamente basado en la respuesta de Jesse:

// Create the set binder. 
Multibinder<MyInterface> interfaceBinder = Multibinder.newSetBinder(binder(), MyInterface.class, MyBinding.class); 

// Load each module that is defined as a service. 
for (final MyModule module : ServiceLoader.load(MyModule.class)) { 

    // Generate a key with a unique random name, so it doesn't interfere with other bindings. 
    final Key<MyInterface> myKey = Key.get(MyInterface.class, Names.named(UUID.randomUUID().toString())); 
    install(new PrivateModule() { 
    @Override protected void configure() { 
     // Install the module as part of a PrivateModule so they have full hands on their own implementation. 
     install(module); 
     // Bind the unique named key to the binding of MyInterface. 
     bind(myKey).to(MyInterface.class); 
     // Expose the unique named binding 
     expose(myKey); 
    } 
    }); 
    // bind the unique named binding to the set 
    interfaceBinder.addBinding().to(myKey); 
} 

Esto me permite no obliga al "cliente" para extender una PrivateModule, sino más bien utilizar cualquier aplicación módulo si MyModule es una interfaz que se extiende Módulo.

Respuesta

9

Parece que tendrá que pasar por algunos aros para promite enlaces desde el módulo privado para que puedan estar en el multibinding del inyector de nivel superior.

esto debería funcionar:

public class SquareModule extends AbstractModule { // does not extend PrivateModule 
    @Overide public void configure() { 
    // this key is unique; each module needs its own! 
    final Key<MyInterface> keyToExpose 
     = Key.get(MyInterface.class, Names.named("square")); 

    install(new PrivateModule() { 
     @Override public void configure() { 

     // Your private bindings go here, including the binding for MyInterface. 
     // You can install other modules here as well! 
     ... 

     // expose the MyInterface binding with the unique key 
     bind(keyToExpose).to(MyInterface.class); 
     expose(keyToExpose); 
     } 
    }); 

    // add the exposed unique key to the multibinding 
    Multibinder.newSetBinder(binder(), MyInterface.class).addBinding().to(keyToExpose); 
    } 
} 

Esta solución es necesaria porque multibindings tienen que suceder en el inyector de nivel superior. Pero los enlaces de módulos privados no son visibles para ese inyector, por lo que debe exponerlos.

+0

Gracias Jesse, sin embargo, después de leer su respuesta, no estaba seguro de haber explicado correctamente el problema. Así que corregí mi pregunta con un ejemplo. ¿Es esto más comprensible ahora? –

+0

Ok, voy a probar su nueva respuesta e informar el resultado aquí. –

+0

Sí, funciona perfectamente. Es bastante difícil hacerlo modularizable, pero está bien. –