Статьи

Хорошее использование затворов

Суд-Closure Не так давно в своем блоге я объяснил, что такое Closure в Groovy. Этот пост в блоге объяснит один хороший пример их использования. Недавно я обнаружил, что должен написать ту же логику обработки исключений для нескольких API-интерфейсов контроллеров, которые обслуживают запросы AJAX. Это было так:

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) и обрабатывать ваши общие исключения там. Опять же, возможно, но только больше кода. До следующего раза, береги себя.

Ссылка: хорошее использование Closures от нашего партнера JCG Алекса Стейвли в блоге Tech Blog в Дублине .