Как вы используете общие методы в разных контроллерах Grails?
В настоящее время, когда мне нужно использовать метод типа processParams(params)
между разными контроллерами, я использую либо наследование, либо службы.
Оба решения имеют некоторые неудобства:
- С наследованием вы не можете использовать множественное наследование, а это значит, что вам нужно иметь все методы утилиты контроллера в одном месте. Кроме того, есть ошибка в grails, которая не обнаруживает никаких изменений кода в классах базового контроллера в режиме разработки (вам нужно перезапустить приложение).
- С помощью служб у вас нет доступа ко всем инъецированным свойствам, таким как params, session, flush...
Итак, мой вопрос: есть ли другой способ использовать некоторые распространенные методы, доступные для нескольких контроллеров?
Ответы
Ответ 1
Один из вариантов, который мне нравится, заключается в том, чтобы писать общие методы как категорию, а затем при необходимости смешивать их с контроллерами. Это дает гораздо большую гибкость, чем наследование, имеет доступ к таким файлам, как params, и код прост и понятен.
Вот крошечный пример:
@Category(Object)
class MyControllerCategory {
def printParams() {
println params
}
}
@Mixin(MyControllerCategory)
class SomethingController {
def create = {
printParams()
...
}
def save = {
printParams()
}
}
Ответ 2
Это не поможет вам перезапустить процесс разработки, но это так, как я решил эту проблему. Это уродливая и, вероятно, не очень хорошая практика, но я ставлю общий код на классы как закрытие. Тогда я могу сделать что-то вроде:
new ControllerClosures().action(this)
а с помощью в классе controllerClosures
def action={
it.response.something
return [allYourData]
}
Ответ 3
Общая функциональность - это вызов нового класса, не обязательно общего предка. В формулировке вопроса отсутствует выражение об ответственности за него. Само собой разумеется, это единственная ответственность, за которую мы создаем новый класс. Я принимаю дальнейшие решения на основе ответственности класса.
Я предпочитаю гибрид ответов robbbert и Jared: я создаю дополнительные классы, передавая им необходимые внутренние элементы контроллера в качестве параметров. Иногда классы развиваются из объектов метода.
Как:
def action = {
def doer = SomeResponsibilityDoer(this.request, this.response)
render doer.action()
}
Не так кратковременно, но позволяет получить код в тестах и поддерживать связь на низком уровне.
Поскольку SomeResponsibilityDoer
будет только иметь пару полей - запросить ответ - это не большая сделка, строящая его с каждым запросом.
Это также не имеет большого значения, когда SomeResponsibilityDoer
не перезагружается при смене контроллера в dev, потому что:
- Изначально вы можете объявить его в некоторых файлах Controller - он будет перезагружен. После того, как вы закончите его, мы надеемся, что он не изменится часто, поэтому переместите его на
src/groovy
.
- Еще важнее то, что быстрее и лучше разрабатывать дизайн при модульных тестах, чем при запуске приложения и перезагрузке Contoller.
Ответ 4
Вы можете использовать шаблон дизайна делегирования:
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()
В случае контроллера назначьте вспомогательный класс непосредственно в поле экземпляра (или в конструкторе с нулевым значением):
class HelperClass {
def renderFoo() { render 'foo' }
}
class FooController {
private @Delegate HelperClass helperClass = new HelperClass()
def index = { this.renderFoo() }
}
Информация типа delegate скомпилируется в содержащий класс.
Ответ 5
Вы можете написать общий метод в commonService
и использовать эту службу для вызова метода commmon.