Создание контроллеров Grails более DRY?

Я ищу способы очистки моего кода контроллера Grails. В разных контроллерах я более или менее придерживаюсь той же логики.

  • получить объект
  • проверить, существует ли он
  • и т.д..

Есть ли способ, с помощью которого действия контроллера повторяют общий код?

--- решение ---

Все ответы на вопрос внесли вклад в решение, которое мы внедрили.

Мы создали класс, который используется в наших контроллерах с использованием подхода Mixin. Одним из методов, который предоставляет mixin, является метод withObject. Этот метод принимает имя домена из контроллера и использует эту базу для этого метода. Конечно, это поведение можно переопределить!

def withObject(object=this.getClass().getName()-"Controller", id="id", Closure c) {
    assert object
    def obj =  grailsApplication.classLoader.loadClass(object).get(params[id])
    if(obj) {
        c.call obj
    } else {
        flash.message = "The object was not found"
        redirect action: "list"
    }
}

Таким образом, все ответы способствовали решению! Большое спасибо!

Ответ 1

Я всегда вытаскиваю этот пост в блоге, когда возникает этот вопрос:

http://mrpaulwoods.wordpress.com/2011/01/23/a-pattern-to-simplify-grails-controllers/

В основном у вас есть частный помощник для различных доменов в ваших контроллерах.

private def withPerson(id="id", Closure c) {
    def person = Person.get(params[id])
    if(person) {
        c.call person
    } else {
        flash.message = "The person was not found."
        redirect action:"list"
    }
}

То, как вы кодируете получателя, очень гибко и типичное использование для меня (это не описано в блоге) предназначено для редактирования и т.д.

Я обычно кодирую этот путь (мне нравится шаблон для его четкого разделения и читаемости):

 def editIssue() {
    withIssue { Issue issue ->
        def issueTypes = IssueTypeEnum.values().collect {it.text }
        [issueTypes:issueTypes,activePage:"issue", issue: issue]
    }
}

 def doEditIssue(IssueCommand cmd) {
    if(cmd.validate()) {
        withIssue { Issue issue ->
            issue.updateIssue(cmd)
            redirect(action: "show", id: issue.id)
        }
    }
    else {
        def issueTypes = IssueTypeEnum.values().collect {it.text }
        render(view: "edit", model:[issueTypes:issueTypes,issue:cmd,activePage:"issue"])
    }
}

С моим помощником-получателем:

private def withIssue( Closure c) {
    def issue = Issue.get(params.id)
    if(issue) {
        c.call issue
    }
    else {
        response.sendError(404)
    }
}

Я действительно думаю, что метод mixin (очень похожий на метод "продлить общий абстрактный контроллер" ) тоже хорош, но этот способ дает два преимущества:

  • Вы можете ввести помощника, как видите, я делаю в закрытии, предоставляя вам доступ к методам и т.д. в STS/IDEA (не протестированные Netbeans).
  • Повторение не очень велико и возможность изменять getter (использовать, например, BarDomain.findByFoo(params.id) и т.д.)

В представлении, которое я связываю для редактирования(), я просто помещаю id="${issue.id}" в <g:form>, и он работает без проблем.

Ответ 2

Я бы не рекомендовал наследование для этого, так как вы не можете распространять общие методы в нескольких суперклассах. Ваш абстрактный класс быстро станет беспорядочным, если у вас много контроллеров. Вы не можете использовать композицию (например, используя Сервис), потому что у вас нет доступа к response, render или params прямо оттуда.

Подход, который я использую, заключается в том, чтобы вводить общие методы через Mixins.

@Mixin(ControllerGenericActions)
@Mixin(ControllerUtil)
class BookController {
  def show = &genericShow.curry(Book)

  def exists = {
    render(idExists(Book))
  }
}

Первое действие show использует общий метод в ControllerGenericActions.groovy с аргументом, связанным с ним. Второе использование метода mixin idExists находится внутри действия контроллера.

Вот пример кода для src/groovy/ControllerGenericActions.groovy

class ControllerGeneric {
  def genericShow(Class clazz) {
    render clazz.get(params.id) as XML
  }
}

и в src/groovy/ControllerUtil.groovy

class ControllerUtil {
  def idExists (Class clazz) {
    return clazz.get(params.id) != null
  }

Не очень полезно в этом случае, но вы получаете идею.

Ответ 3

Внедрить абстрактный контроллер с общими методами (используйте "защищенную" директиву) и расширить от него свои настоящие контроллеры. Не используйте слова "get" и "set" в начале этих имен методов. Нехорошо, но это работает.