Привет, Хабр! Меня зовут Владислав Родин. В настоящее время я являюсь руководителем курса «Архитектор высоких нагрузок» в OTUS, а также преподаю на курсах, посвященных архитектуре ПО.

Специально к старту нового набора на курс «Архитектура и шаблоны проектирования» я продолжаю серию своих публикаций про шаблоны 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'ом.



Узнать о курсе подробнее