
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
class ApiRugbyPlayerController { JSON getPlayerStats() { try { ... // invoke business service method to get player stats } catch (ServiceException serviceException) { // don't care too much about this. // log and move on ... } catch (SessionException sessionException) { // clear out session cookies ... // send 403 to client ... } catch (Exception ex) { throw new ApiException(ex) } } JSON updatePlayerStats() { try { ... // invoke business service method to update player stats } catch (ServiceException serviceException) { // don't care too much about this. // log and move on ... } catch (SessionException sessionException) { // clear out session cookies ... // send 403 to client ... } catch (Exception ex) { throw new ApiException(ex) } } JSON queryPlayerStats(){ try { ... // invoke business service method to query player stats } catch (ServiceException serviceException) { // don't care too much about this. // log and move on ... } catch (SessionException sessionException) { // clear out session cookies ... // send 403 to client ... } catch (Exception ex) { throw new ApiException(ex) } }} |
Как видно, здесь происходит некоторое дублирование кода. В духе СУХОГО (не повторяйте себя), лучше всего определить эту логику обработки исключений один раз, а затем повторно использовать ее. Поэтому я определил следующий служебный метод, который реализует шаблон обработки исключений и принимает замыкание, для которого выполняется обработка исключений.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
private JSON withExceptionHandling(Closure c) { try { ... c.call(); } catch (ServiceException serviceException) { // don't care too much about this. // log and move on ... } catch (SessionException sessionException) { // clear out session cookies ... // send 403 to client ... } catch (Exception ex) { throw new ApiException(ex) } } |
Мы можем сделать блок кода замыканием в Groovy, окружив его {}. Это означает, что я могу превратить логику в моих методах контроллера в замыкания и передать их в мой служебный метод. И когда я передаю его в свой служебный метод, мне даже не нужно передавать его внутри (), так как Groovy не делает вас. Это означает, что я могу убрать всю обычную обработку исключений, удалить раздувшийся код, и мой Controller API стал намного чище.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
class ApiRugbyPlayerController { JSON getPlayerStats() { withExceptionHandling { ... // invoke business service method to get player stats } } JSON updatePlayerStats() { withExceptionHandling { ... // invoke business service method to update player stats } } JSON queryPlayerStats(){ withExceptionHandling { ... // invoke business service method to query player stats } } private JSON withExceptionHandling(Closure c) { try { ... c.call(); } catch (ServiceException serviceException) { // don't care too much about this. // log and move on ... } catch (SessionException sessionException) { // clear out session cookies ... // send 403 to client ... } catch (Exception ex) { throw new ApiException(ex) } }} |
Итак, поехали. Мы придерживались принципов DRY, избегали раздувания кода и выделяем место для обработки исключений, будучи уверенными в том, что оно реализуется последовательно. Этот пример закрытия Groovy немного похож на вызов второго порядка в JavaScript. Если бы мы хотели сделать что-то похожее на Java, это было бы просто намного больше кода. Мы могли бы использовать что-то вроде шаблона команды и поместить их выполнение в логику обработки исключений. У вас было бы больше развязки, но у вас гораздо больше кода. Или вы можете заставить все ваши AJAX API вводить общий метод (например, Front Controller) и обрабатывать ваши общие исключения там. Опять же, возможно, но только больше кода. До следующего раза, береги себя.