Шаблоны GRASP: Controller (контроллер)
Привет, Хабр! Меня зовут Владислав Родин. В настоящее время я являюсь руководителем курса «Архитектор высоких нагрузок» в OTUS, а также преподаю на курсах, посвященных архитектуре ПО.
Специально к старту нового набора на курс «Архитектура и шаблоны проектирования» я продолжаю серию своих публикаций про шаблоны GRASP.
Описанные в книге Craig'а Larman'а «Applying UML and patterns, 3rd edition», GRASP'овские паттерны являются обобщением GoF'овских паттернов, а также непосредственным следствием принципов ООП. Они дополняют недостающую ступеньку в логической лестнице, которая позволяет получить GoF'овские паттерны из принципов ООП. Шаблоны GRASP являются скорее не паттернами проектирования (как GoF'овские), а фундаментальными принципами распределения ответственности между классами. Они, как показывает практика, не обладают особой популярностью, однако анализ спроектированных классов с использованием полного набора GRASP'овских паттернов является необходимым условием написания хорошего кода.
Полный список шаблонов GRASP состоит из 9 элементов:
В прошлый раз мы обсудили целых два принципа Low Coupling и High Cohesion. Сейчас я предлагаю рассмотреть еще один достаточно важный принцип GRASP, позволяющий установить правила обработки входящих системных событий. Ниже поговорим о том, что из себя представляет Controller.
Обязанности по обработке входящих системных сообщений необходимо делегировать специальному объекту Controller'у. Controller — это объект, который отвечает за обработку системных событий, и при этом не относится к интерфейсу пользователя. Controller определяет методы для выполнения системных операций.
В системе необходимо обрабатывать входящие системные события (пользователи нажимают на кнопки, срабатывают таймеры и так далее). Это можно сделать либо в отдельном объекте (Controller), либо разбросать по всему коду. Например, в нужном месте из бизнес — логики можно слазить и узнать нажал пользователь на кнопку или нет. Второй подход согласно изучаемому шаблону является неправильным. Дело в том, что для обработки многопоточного входа (а ввод пользователя всегда многопоточен) необходимо писать и поддерживать многопоточный код. Таким образом, в коде нашей бизнес — логики появляются технические нюансы синхронизации (мьютексы, семафоры, synchronized — блоки и так далее). Наличие всего этого добра сбивает с толку программиста, читающего код бизнес — логики. Более того, применение таких приемов придется дублировать в нескольких участках кода бизнес — логики. Помимо этого, наличие многопоточности усложняет код приложения на порядок. Многопоточный код сложно покрыть юнит — тестами.
Идея шаблона состоит в том, чтобы не размазывать этот сложный код по всему приложению. Такой объект как Controller будет превращать многопоточный вход в один поток. Код бизнес — логики будет однопоточным. Шаблон не описывает то, как Conroller будет превращать многопоточный вход в один поток, но он должен это так или иначе делать (ставить запросы в очередь, создавать свою копию объекта бизнес — логики для каждого потока).
В Controller'е удобно накапливать информацию о системных событиях (в случае, если системные операции выполняются в некоторой определенной последовательности). Controller может содержать логирование, аутентификацию, авторизацию, кеширование.
Имеет месте улучшение условий для повторного использования компонентов. Когда вся многопоточность сосредоточена в этом самом Conroller'е, мы можем переключить код бизнес — логики на другой вход, просто дописав другой Conroller (добавить поддержку веб-сервиса, перенести код в однопоточную среду).
Controller может оказаться перегруженным. В связи с этим, conroller может из себя представлять package или отдельный компонент.
Controller в некотором смысле может быть рассмотрен в контексте гексагональной архитектуры в качестве защиты от пользовательского ввода. Данная архитектура предполагает, что наша система (бизнес — логика) живет во «враждебном окружении». Она как — то взаимодействует с базами данных, получает пользовательский ввод, конфигурацию, осуществляет вывод и так далее. Каждый элемент может быть «враждебным»: база данных может отвалиться, конфигурация может быть невалидной или отсутствовать вовсе, вывод может быть заблокирован, ввод может быть некорректным. Для корректной обработки всех таких сценариев предполагается защита нашей бизнес — логики некоторыми адаптерами и фасадами.
Многопоточность в коде бизнес — логики является злом, усложняющим этот код на порядок. Ее следует избегать, выделяя объект, превращающий многопоточный вход в однопоточный какими — либо способами. Данный объект называется Controller'ом.
Узнать о курсе подробнее
Специально к старту нового набора на курс «Архитектура и шаблоны проектирования» я продолжаю серию своих публикаций про шаблоны GRASP.
Введение
Описанные в книге Craig'а Larman'а «Applying UML and patterns, 3rd edition», GRASP'овские паттерны являются обобщением GoF'овских паттернов, а также непосредственным следствием принципов ООП. Они дополняют недостающую ступеньку в логической лестнице, которая позволяет получить GoF'овские паттерны из принципов ООП. Шаблоны GRASP являются скорее не паттернами проектирования (как GoF'овские), а фундаментальными принципами распределения ответственности между классами. Они, как показывает практика, не обладают особой популярностью, однако анализ спроектированных классов с использованием полного набора GRASP'овских паттернов является необходимым условием написания хорошего кода.
Полный список шаблонов GRASP состоит из 9 элементов:
- Information Expert
- Creator
- Controller
- Low Coupling
- High Cohesion
- Polymorphism
- Pure Fabrication
- Indirection
- Protected Variations
В прошлый раз мы обсудили целых два принципа Low Coupling и High Cohesion. Сейчас я предлагаю рассмотреть еще один достаточно важный принцип GRASP, позволяющий установить правила обработки входящих системных событий. Ниже поговорим о том, что из себя представляет Controller.
Controller
Формулировка
Обязанности по обработке входящих системных сообщений необходимо делегировать специальному объекту Controller'у. Controller — это объект, который отвечает за обработку системных событий, и при этом не относится к интерфейсу пользователя. Controller определяет методы для выполнения системных операций.
Мотивировка
В системе необходимо обрабатывать входящие системные события (пользователи нажимают на кнопки, срабатывают таймеры и так далее). Это можно сделать либо в отдельном объекте (Controller), либо разбросать по всему коду. Например, в нужном месте из бизнес — логики можно слазить и узнать нажал пользователь на кнопку или нет. Второй подход согласно изучаемому шаблону является неправильным. Дело в том, что для обработки многопоточного входа (а ввод пользователя всегда многопоточен) необходимо писать и поддерживать многопоточный код. Таким образом, в коде нашей бизнес — логики появляются технические нюансы синхронизации (мьютексы, семафоры, synchronized — блоки и так далее). Наличие всего этого добра сбивает с толку программиста, читающего код бизнес — логики. Более того, применение таких приемов придется дублировать в нескольких участках кода бизнес — логики. Помимо этого, наличие многопоточности усложняет код приложения на порядок. Многопоточный код сложно покрыть юнит — тестами.
Решение
Идея шаблона состоит в том, чтобы не размазывать этот сложный код по всему приложению. Такой объект как Controller будет превращать многопоточный вход в один поток. Код бизнес — логики будет однопоточным. Шаблон не описывает то, как Conroller будет превращать многопоточный вход в один поток, но он должен это так или иначе делать (ставить запросы в очередь, создавать свою копию объекта бизнес — логики для каждого потока).
Преимущества
В Controller'е удобно накапливать информацию о системных событиях (в случае, если системные операции выполняются в некоторой определенной последовательности). Controller может содержать логирование, аутентификацию, авторизацию, кеширование.
Имеет месте улучшение условий для повторного использования компонентов. Когда вся многопоточность сосредоточена в этом самом Conroller'е, мы можем переключить код бизнес — логики на другой вход, просто дописав другой Conroller (добавить поддержку веб-сервиса, перенести код в однопоточную среду).
Недостатки
Controller может оказаться перегруженным. В связи с этим, conroller может из себя представлять package или отдельный компонент.
Аналогия с гексагональной архитектурой
Controller в некотором смысле может быть рассмотрен в контексте гексагональной архитектуры в качестве защиты от пользовательского ввода. Данная архитектура предполагает, что наша система (бизнес — логика) живет во «враждебном окружении». Она как — то взаимодействует с базами данных, получает пользовательский ввод, конфигурацию, осуществляет вывод и так далее. Каждый элемент может быть «враждебным»: база данных может отвалиться, конфигурация может быть невалидной или отсутствовать вовсе, вывод может быть заблокирован, ввод может быть некорректным. Для корректной обработки всех таких сценариев предполагается защита нашей бизнес — логики некоторыми адаптерами и фасадами.
Вывод
Многопоточность в коде бизнес — логики является злом, усложняющим этот код на порядок. Ее следует избегать, выделяя объект, превращающий многопоточный вход в однопоточный какими — либо способами. Данный объект называется Controller'ом.
Узнать о курсе подробнее