2010-11-16 8 views
26

Actualmente, cuando necesito compartir un método como processParams(params) entre diferentes controladores, utilizo la herencia o los servicios. Tanto solución tiene algunos inconvenientes:¿Cómo compartas métodos comunes en diferentes controladores de grails?

  • Con la herencia, no se puede utilizar la herencia múltiple que significa que es necesario tener todos sus métodos de utilidad del controlador en un solo lugar. Y también, hay un error en Grails que no detecta ningún cambio de código en las clases del Controlador base en modo de desarrollo (necesita reiniciar la aplicación)
  • Con servicios, no tiene acceso a todas las propiedades inyectadas como params, sesión, al ras ...

Así que mi pregunta es: ¿hay alguna otra manera de utilizar algunos métodos comunes accesibles para múltiples controladores?

Respuesta

3

Esto no ayuda a reiniciar en modo de desarrollo el problema que tiene, pero es la forma en que he resuelto este problema. Es feo y probablemente no sea una buena práctica, pero factorizo ​​el código común en clases como cierres. Entonces puedo hacer algo como:

new ControllerClosures().action(this) 

y desde la de la clase controllerClosures

def action={ 
    it.response.something 
    return [allYourData] 
} 
2

Puede utilizar el Delegation design pattern:

class Swimmer { 
    def swim() { "swimming" } 
} 

class Runner { 
    def run() { "running" } 
} 

class Biker { 
    def bike() { "biking" } 
} 

class Triathlete { 
    @Delegate Swimmer swimmer 
    @Delegate Runner runner 
    @Delegate Biker biker 
} 

def triathlete = new Triathlete(
    swimmer: new Swimmer(), 
    runner: new Runner(), 
    biker: new Biker() 
) 

triathlete.swim() 
triathlete.run() 
triathlete.bike() 

En caso de un controlador, asigne el ayudante clase directamente en el campo de instancia (o en el constructor nullary):

class HelperClass { 
    def renderFoo() { render 'foo' } 
} 

class FooController { 
    private @Delegate HelperClass helperClass = new HelperClass() 

    def index = { this.renderFoo() } 
} 

La información del tipo delegate se compila en la clase contenedora.

+6

La clase delegado no tiene acceso a la parte interna de la clase propietario, como el método o params render . – ataylor

+2

Desafortunadamente es cierto, y por lo tanto '@ Delegado' no será la metodología de elección en este caso. – robbbert

22

Una opción que me gusta es escribir los métodos comunes como una categoría, luego mezclarlos en los controladores según sea necesario. Le da mucha más flexibilidad que herencia, tiene acceso a cosas como params, y el código es simple y comprensible.

Aquí hay un pequeño ejemplo:

@Category(Object) 
class MyControllerCategory { 
    def printParams() { 
     println params 
    } 
} 

@Mixin(MyControllerCategory) 
class SomethingController { 

    def create = { 
     printParams() 
     ... 
    } 

    def save = { 
     printParams() 
    } 
} 
+7

En realidad, no necesita la anotación '@ Category' para usar la anotación' @ Mixin'. - Solo lo necesitarías cuando uses el método alternativo 'SomethingController.mixin ([MyControllerCategory])' (que requiere 'MyControllerCategory' para proporcionar un método' static' 'printParams()'). Los documentos de Groovy proporcionan muestras similares a las suyas, pero esto no es necesario. - Este malentendido podría deberse al hecho de que el uso de * categorías * ha sido sometido a un proceso evolutivo, una vez torpe por la sintaxis, y ahora reemplazado por la anotación '@ Mixin'. – robbbert

+2

Finalmente, la principal diferencia entre '@ Mixin' y' @ Delegate' es que los métodos * delegate * se compilan estáticamente en la clase objetivo, mientras que los métodos * mixin * se invocan en tiempo de ejecución. – robbbert

+0

¿Hay alguna manera de hacer 'MyControllerCategory' sujeto a la inyección de dependencia? –

3

funcionalidad común es un llamado a una nueva clase, no necesariamente ancestro común. A la formulación de la pregunta le falta una declaración de responsabilidad. No hace falta decir que es una responsabilidad única para la que creamos una nueva clase. Tomo más decisiones basadas en la responsabilidad de clase.

prefiero un híbrido de robbbert 's y Jared' s respuestas: construyo clases adicionales, pasándolos componentes internos del controlador necesarios como parámetros. Algunas veces las clases se desarrollan desde method objects. Al igual que :

def action = { 
    def doer = SomeResponsibilityDoer(this.request, this.response) 
    render doer.action() 
} 

No es tan breve, pero le permite obtener código bajo pruebas y mantener bajo acoplamiento.

Como SomeResponsibilityDoer solo va a tener un par de campos, solicite una respuesta, no es gran cosa construirlo con cada solicitud.

Asimismo, no es un gran problema que tiene SomeResponsibilityDoer no vuelve a cargar sobre el cambio en el controlador de dev, porque:

  1. Inicialmente, se puede declarar que en algunos de los archivos del controlador - que se volverá a cargar. Después de completarlo, con suerte no cambiará a menudo, así que muévalo al src/groovy.
  2. Aún más importante, es más rápido y mejor para el diseño desarrollar bajo pruebas unitarias que bajo la aplicación que ejecuta y recarga un Contoller.
1

Puede escribir todo el método común en commonService y utilizar ese servicio a envoke método commmon

Cuestiones relacionadas