2012-05-24 14 views

Respuesta

6

Cuando se procesa una vista en una acción del controlador, que acaba de invocar una función sencilla que ha sido generado por el motor de plantillas:

public Application extends Controller { 
    public static Result index() { 
    return ok(views.html.index.render(42)); 
    } 
} 

Aquí, render es un método del objeto index la que tiene el tipo Template1<Integer, Html> .

Ahora la pregunta es: ¿cómo escribir un controlador genérico capaz de invocar una vista específica para otro controlador? O simplemente: cómo abstraer con vistas?

veo dos soluciones: inversion of control y reflexión.

Veamos cómo implementar ambos en un caso de uso simple. Digamos que tiene la siguiente genérica clase Shower<T> capaz de calcular una respuesta HTTP que contiene una representación HTML de cualquier valor de tipo T:

public class Shower<T> { 
    public Result show(T value) { 
    // TODO return an HTML representation of `value` 
    } 
} 

Inversión de Control

Para implementar Shower<T> mediante la inversión de control sólo tenemos que inyectar el valor Template1<T, Html> utilizada para realizar la prestación:

public class Shower<T> { 

    public final Template1<T, Html> template; 

    public Shower(Template1<T, Html> template) { 
    this.template = template; 
    } 

    public Result show(T value) { 
    return ok(template.render(value)); 
    } 

} 

utilizarlo en un controlador, crear una instancia estática de Shower<T> e inyectarlo la plantilla a utilizar:

public class Application extends Controller { 
    public static Shower<Foo> foo = new Shower<Foo>(views.html.Foo.show.ref()); 
} 

Reflexión

puede que le resulte demasiado repetitivo a tener que inyectar de forma explícita la plantilla a utilizar para cada instancia de Shower<T>, por lo que puede tener la tentación de recuperarlo por reflexión, basado en una convención de nomenclatura, por ejemplo para mostrar un valor de tipo Foo, sólo tiene que buscar un objeto denominado show en el paquete views.html.Foo:

public class Shower<T> { 

    private final Class<T> clazz; 

    public Shower(Class<T> clazz) { 
    this.clazz = clazz; 
    } 

    public Result show(T value) throws Exception { 
    Class<?> object = Play.application().classLoader().loadClass("views.html." + clazz.getSimpleName() + ".show$"); 
    Template1<T, Html> template = (Template1<T, Html>)object.getField("MODULE$").get(null); 
    return ok(template.render(value)); 
    } 
} 

(esa es la manera de acceder a Scala objetos utilizando la reflexión)

Se puede utilizar de la siguiente manera en una controlador:

public class Application extends Controller { 
    public static Shower<Foo> foo = new Shower<Foo>(Foo.class); 
} 

Pros y contras

La solución basada en la reflexión requiere menos texto estándar en el sitio de llamadas, pero el hecho de que se basa en una convención de nomenclatura lo hace más frágil. Además, esta solución solo fallará en el tiempo de ejecución cuando fallará, mientras que la primera solución mostrará las plantillas que faltan en el momento de la compilación.Por último, pero no menos importante, la solución basada en la reflexión puede agregar un poco de sobrecarga de rendimiento debido a la reflexión.

+0

'ref()'! oh, este conocimiento va a ser extremadamente útil! Muchas gracias por explicar esto! – Daniel

+0

¡Me gusta su solución de inversión de control! Pero si tienes varios métodos en tu 'Ducha 'también debes pasar múltiples vistas. ¿Sería útil usar también el [Patrón del constructor] (http://en.wikipedia.org/wiki/Builder_pattern) para ese problema? (ver también: Bloch, J. _Eficaz Java_ -> Elemento 2) –

+0

Hasta donde yo entiendo, este patrón le permite escribir un código más legible en el sitio de llamadas, pero aún así requiere que pase todas las vistas. –

Cuestiones relacionadas