2011-11-25 11 views
11

Tôi đang tìm cách dọn dẹp mã bộ điều khiển Grails của mình. Trong các bộ điều khiển khác nhau tôi ít nhiều có cùng một logic ..Làm cho bộ điều khiển Grails có nhiều DRY hơn không?

  • lấy đối tượng
  • kiểm tra nếu nó tồn tại
  • vv ..

Có cách nào gợi ý vào việc hành động điều khiển sử dụng lại mã phổ biến?

--- giải pháp ---

Tất cả câu trả lời cho câu hỏi đã góp phần vào giải pháp mà chúng tôi đã triển khai.

Chúng tôi đã tạo một lớp được sử dụng trong bộ điều khiển của chúng tôi bằng cách sử dụng phương pháp Mixin. Một trong những phương thức mà mixin thể hiện là phương thức withObject. Phương thức này lấy tên miền từ bộ điều khiển và sử dụng cơ sở này cho phương thức. Hành vi này có thể được ghi đè tất nhiên!

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" 
    } 
} 

Vì vậy, tất cả các câu trả lời đều góp phần vào giải pháp! Cảm ơn rất nhiều!

+4

Vui lòng chỉnh sửa câu hỏi và thêm ví dụ về mã của bạn. Tại thời điểm này câu hỏi là rất mơ hồ. – ordnungswidrig

Trả lời

8

tôi luôn luôn kéo ra bài viết trên blog này khi câu hỏi này đi lên:

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

Về cơ bản bạn có một helper riêng cho lĩnh vực khác nhau trong bộ điều khiển của bạn.

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" 
    } 
} 

Cách bạn mã getter rất linh hoạt và sử dụng điển hình cho tôi (không nằm trong blog) là để chỉnh sửa, v.v ...

Tôi thường code theo cách này (tôi thích mô hình cho sự phân chia rõ ràng và dễ đọc):

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"]) 
    } 
} 

Với getter helper của tôi hạnh phúc:

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

tôi nghĩ rằng phương pháp mixin (rất giống với cách 'mở rộng bộ điều khiển trừu tượng thông thường') cũng tốt, nhưng cách này mang lại hai lợi thế:

  1. Bạn có thể gõ helper, giống như bạn thấy tôi làm trong đóng cửa cho bạn truy cập vào các phương pháp vv trong STS/IDEA (không được thử nghiệm Netbeans)
  2. Sự lặp lại không phải là rất cao, và khả năng thay đổi getter (để sử dụng cho Ví dụ BarDomain.findByFoo (params.id) vv)

trong giao diện tôi ràng buộc để chỉnh sửa() tôi chỉ cần đặt một id="${issue.id}" trong <g:form> và nó hoạt động trơn tru.

+0

Cảm ơn bạn đã bình luận nhiều hay ít kết hợp một vài câu trả lời trong chuỗi này cho một giải pháp làm việc ở bên cạnh tôi. Tôi sử dụng chiến lược mixin cùng với phương thức withObject chung chung bằng cách nào đó. Tôi sẽ cập nhật câu hỏi của mình với mã mà tôi đang sử dụng ngay bây giờ. – Marco

0

Triển khai bộ điều khiển trừu tượng bằng các phương pháp phổ biến (sử dụng chỉ thị 'được bảo vệ') và mở rộng từ bộ điều khiển thực của bạn. Không sử dụng các từ 'get' và 'set' ở đầu tên của phương thức này. Không tốt, nhưng nó hoạt động.

+1

Tại sao bạn không đề xuất sử dụng các từ 'get' và 'set' ở đầu tên của phương thức này? – gotomanners

+0

để tránh xác định chúng là 'getter' hoặc 'setter'. – jenk

6

Tôi sẽ không đề xuất thừa kế cho điều đó, vì bạn không thể phát tán các phương thức chung trong một số lớp siêu. Lớp trừu tượng của bạn sẽ nhanh chóng trở nên lộn xộn nếu bạn có nhiều bộ điều khiển. Bạn không thể sử dụng bố cục (ví dụ: sử dụng Dịch vụ) vì bạn không có quyền truy cập vào số response, render hoặc params trực tiếp từ đó.

Cách tiếp cận tôi sử dụng là tiêm các phương pháp chung thông qua Mixins.

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

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

Hành động đầu tiên show sử dụng một phương pháp chung trong ControllerGenericActions.groovy, với một cuộc tranh cãi binded đến nó. Việc sử dụng thứ hai của một phương thức mixin idExists là bên trong một hành động điều khiển.

Dưới đây là một ví dụ mã cho src/groovy/ControllerGenericActions.groovy

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

và trong src/groovy/ControllerUtil.groovy

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

Không rất hữu ích trong trường hợp này, nhưng bạn sẽ có được ý tưởng.

+0

Tôi không thực sự sử dụng các bao đóng, vì vậy hãy tha thứ nếu tôi mắc lỗi. Nhưng nếu một phương pháp được tạo ra như 'genericShow (Class clazz)' bạn có thể cà ri phương pháp đó không? Tôi luôn ấn tượng rằng một phương pháp không thể bị quấy rầy nhưng có thể đóng cửa. – Marco

+0

Đó là lý do để sử dụng và trước tên phương thức: nó biến một phương thức thành một đóng. – Antoine

+0

Tôi có câu hỏi về giải pháp này: Bộ điều khiển mixin ControllerGeneric có thể kiểm tra được không? Tôi hỏi vì 'render' là một phương thức được Grails cung cấp trong khi tăng cường bộ điều khiển –