Аспектно-ориентированное программирование в JavaScript
Нет, мы не опечатались — оно существует. А умеет такое, с чем совладают немногие разрабы: рассказываем, что — и как не хватить с этим лиха.
скриншот из игры necromunda: hired gun / streum on studio
Фернандо Дольо
(Fernando Doglio)
об авторе
Топовый автор Medium. Пишет о технологиях, об образе жизни, личном опыте и многом другом.
Кто не знает про объектно-ориентированное программирование, да и функциональное явно на слуху, а вот об аспектно-ориентированном вы когда-нибудь слышали?
Пусть это и звучит как фраза неисправимого гика, но аспектно-ориентированное программирование (АОП) — источник великой силы. Даже удивительно, почему мы до сих пор прибегаем к нему так редко.
Что самое крутое, АОП легко сочетается как с функциональным, так и с объектно-ориентированным программированием (ООП).
Что ж, разберёмся, что такое АОП и чем оно полезно JavaScript-разработчикам.
Знакомство с АОП
Аспектно-ориентированное программирование позволяет внедрять новый код в готовые функции или объекты, не затрагивая их целевой логики (а следовательно, не влияя на поведение программ).
Предполагается, хотя и не обязательно, что такой код решает некие общие задачи (добавляет так называемую сквозную функциональность). С его помощью можно, скажем, вести журналирование или отладку.
Разберём на примере
Допустим, вы реализовали бизнес-логику и вдруг поняли, что забыли про логирующий код. Тогда, по обыкновению, вы выносите логику журналирования в отдельный модуль, а затем проходитесь по функциям, добавляя информацию для ведения журнала.
А теперь представьте, что с помощью одной-единственной строки кода логгер можно внедрить в любой метод. А срабатывать он будет в некоторые особые моменты, связанные с выполнением этого метода. Здорово?
Аспекты, срезы, советы, или Что, где и когда
Познакомимся с тремя ключевыми понятиями АОП. Они прояснят суть парадигмы и помогут изучать её дальше.
- Аспекты (aspects, «что») — это добавочное поведение, которое нужно включить в целевой код. В контексте JavaScript это функции, которые инкапсулируют нужное нам поведение.
- Срезы (pointcuts, «где») указывают место в целевом коде, куда следует внедрить аспект. Теоретически оно может быть любым, но на практике такое выходит не всегда. И всё же вы можете указывать условия вроде «все методы моего объекта», или «только этот конкретный метод», или даже что-то типа «все методы, которые начинаются с get_».
- Советы (advice, «когда») определяют момент, когда должен выполняться код аспекта, например:
- before (перед вызовом метода),
- after (после возврата в точку вызова),
- around (до вызова метода и при возврате из него),
- whenThrowing (когда метод вызывает исключение) и так далее.
Если совет указывает на время, когда код уже выполнен, аспект перехватит возвращаемое значение и сможет его перезаписать.
Очевидно, что с АОП довольно легко создать библиотеку для добавления журналирования в существующую бизнес-логику, даже если сама она реализована, например, на ООП. Достаточно заменить соответствующие методы целевого объекта на пользовательскую функцию, которая в нужное время добавит логику аспекта, а затем вызовет исходный метод.
Закрепим теорию практикой.
Пример использования
Надеюсь, вам, как и мне, легче учиться на наглядных примерах. Поэтому вот вам реализация метода inject, который добавляет поведение на основе АОП. Убедитесь, как это просто и удобно.
Как видите, я вас не обманул. Хотя код выше охватывает не все варианты применения, он поможет разобраться в следующем примере.
А сперва обратите внимание на реализацию replaceMethod. Вот где творится вся магия. Именно здесь создаётся новая функция, именно тут мы решаем, когда вызывать аспект и что делать со значением, которое он возвращает.
Теперь покажем, как использовать нашу новую библиотеку:
Ничего сверхсложного: один базовый объект с тремя методами. Мы внедрили пару общих для всех них аспектов: один выводит в лог полученные атрибуты, а другой анализирует возвращаемое значение и выводит его тип.
Два аспекта — две строки кода (вместо шести, которые понадобились бы без АОП).
Вот результат, который мы увидим в консоли:
Преимущества аспектно-ориентированного программирования
Разобравшись, что такое АОП и на что оно способно, вы наверняка догадываетесь, почему оно вызывает такой интерес.
Пробежимся по его главным преимуществам.
1. Инкапсуляция сквозной функциональности
Я вообще большой поклонник инкапсуляции — благодаря ей код легче читать и поддерживать, а также использовать повторно во всём проекте.
2. Гибкость логики
Логика, которую вы реализуете в советах и срезах, повышает гибкость при внедрении ваших аспектов. А это, в свою очередь, позволяет вам включать и отключать различные аспекты вашей логики (сорри за каламбур) прямо при выполнении программы.
3. Повторное использование кода
Аспекты — это, по сути, компоненты, небольшие независимые фрагменты кода, которые могут работать где угодно. Если они правильно написаны, то их легко применять в других проектах.
Главная проблема АОП
Ничто не идеально, и АОП тоже есть за что поругать.
В силе этого подхода критики видят и его уязвимость: он скрывает логику и сложность, его применение приводит к побочным эффектам, причину которых трудно понять сходу.
И отчасти противники АОП правы. Эта парадигма позволяет добавить в существующие объекты нехарактерное им поведение или даже заменить всю их логику. Конечно, АОП появилось совсем не для этого, и в примере выше я показывал вовсе не такие «возможности».
Однако АОП позволяет сделать всё, что вы хотите. А такая вседозволенность, помноженная на неопытность разработчика, открывает ящик Пандоры.
Банально это или нет, повторю легендарную фразу из комиксов:
С великой силой приходит и большая ответственность.
Одним словом, АОП — обязывает. Обязывает программиста соответствовать. Это не игрушка для рядового кодера, а выбор продвинутого спеца, который освоил лучшие практики разработки и может правильно сочетать АОП с ними.
Да, в неумелых руках этот инструмент способен навредить, но это едва ли повод от него отказываться. Потому что с АОП можно сделать и много хорошего: например, собрать части общей логики в едином фрагменте и внедрить его куда угодно — одной строкой кода. Как по мне, этот мощный инструмент стоит изучать и применять.
Подытожим
Аспектно-ориентированное программирование отлично дополняет ООП. Благодаря динамической природе JavaScript применять АОП с ним очень легко, как я и показал на примерах.
АОП позволяет выделять код в модули, разделять логику и переиспользовать разработки в других проектах.
Конечно, если применять АОП неумело, то можно всё запутать, но при правильном подходе оно позволяет создавать более чистый и понятный код.