model view controller - ¿Cómo compartas métodos comunes en diferentes controladores de grails?
model-view-controller grails-controller (5)
Actualmente, cuando necesito compartir un método como processParams(params)
entre diferentes controladores, utilizo herencias o servicios. Ambas soluciones tienen algunos inconvenientes:
- Con la herencia, no puede usar herencia múltiple, lo que significa que necesita tener todos sus métodos de utilidad de 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 (debe reiniciar la aplicación)
- Con servicios, no tiene acceso a todas las propiedades inyectadas, como params, sesión, flush ...
Entonces mi pregunta es: ¿hay alguna otra forma de utilizar algunos métodos comunes accesibles para múltiples controladores?
Esto no ayuda a reiniciar en el modo de desarrollo 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 dentro de la clase controllerClosures
def action={
it.response.something
return [allYourData]
}
La funcionalidad común es una llamada para 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 entre las respuestas de robberbert y de Jared : construyo clases adicionales, pasándoles los parámetros internos del controlador necesarios. A veces las clases se desarrollan a partir de objetos de método . Me gusta:
def action = {
def doer = SomeResponsibilityDoer(this.request, this.response)
render doer.action()
}
No es tan breve, pero le permite obtener el código bajo prueba y mantener el acoplamiento bajo.
Como SomeResponsibilityDoer
solo va a tener dos campos, solicite una respuesta, no es gran cosa construirlo con cada solicitud.
Tampoco es un gran problema tener SomeResponsibilityDoer
no recargados en el controlador de cambio en dev, porque:
- Inicialmente, puede declararlo en algunos de los archivos del Controlador; se volverá a cargar. Después de completarlo, con suerte no cambiará a menudo, así que
src/groovy
asrc/groovy
. - 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 se ejecuta y recarga un Contoller.
Puede escribir todo el método común en commonService y usar ese servicio para invocar el método commmon
Puede usar el patrón de diseño de Delegación :
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 el caso de un controlador, asigne la clase auxiliar 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 de tipo del delegate se compila en la clase contenedora.
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()
}
}