Инверсия управления

Одним из наиболее значимых принципов объектно-ориентированного программирования является инверсия управления или принцип инверсии управления. Так же этот принцип в шутку назван «Голливудским принципом», смысл которого раскрывается в определении: «Не звоните нам, мы сами вам позвоним». Данный принцип позволяет существенно снизить связанность между компонентами программного обеспечения, а также изменить процесс его разработки таким образом, чтобы сторонние фреймворки могли использовать компоненты, разрабатываемые другими разработчиками.

Если мы представим себе процесс разработки какого-нибудь проекта, например REST-сервиса, с чистого листа без использования каких-либо фреймворков, то выглядеть он будет следующим образом. Первым делом мы реализуем HTTP-сервер, который будет принимать входящие запросы и маршрутизировать их в компоненты, способные их обработать.

Однако давайте зададим себе вопрос: «А когда в последний раз мы писали собственный HTTP-сервер для REST-сервиса?», наверняка большинство из вас ответит: «Никогда!». Вместо этого мы используем какие-то готовые решения для обслуживания HTTP-запросов, к которым относятся Java EE/Jakarta EE Servlet API, Netty, Spring Framework, Helidon, Micronaut и многие другие фреймворки. Таким образом мы отдаём контроль над потоком исполнения кода сторонним фреймворкам, разрабатывая только специфичные для наших задач компоненты, которые должны быть использованы фреймворками. Именно в передаче контроля над потоком исполнения кода фреймворкам является инверсией управления: фреймворки управляют исполнением кода нашего приложения, а не приложение управляет фреймворками.

Основными способами реализации инверсии управления являются шаблоны проектирования «Шаблонный метод», «Стратегия» и «Локатор служб». Так Servlet API реализует как раз шаблон проектирования «Стратегия», предоставляя интерфейс Servlet, в реализации которого разработчик может разместить свой код для обработки HTTP-запроса. Spring Framework и проекты, входящие в экосистему Spring, тоже широко используют шаблон проектирования «Стратегия»: в интерфейсе HandlerFunction — для обработки HTTP-запросов в функциональном стиле, в UserDetailsService — для получения информации об аутентифицирующемся пользователе и т.д.

В этой короткой статье я не стану углубляться в детали реализаций, так как каждую из них нужно рассматривать по отдельности и подробно, так что это всё будет в следующих статьях.