Эта статья является результатом моих исследований, мыслей и выводов во время разработки AspectJS: AOP-фреймворка для JavaScript.
Прежде чем я влюбился в JavaScript, я писал много кода Java для серверных приложений. Для этой цели Spring framework, несомненно, является одним из наиболее широко используемых инструментов. Очень универсальный, он охватывает широкий спектр вариантов использования: от динамического рендеринга веб-страниц до разработки REST API, обработки постоянства транзакций или аутентификация.
Благодаря таким ключевым принципам проектирования, как внедрение зависимостей или инверсия управления, Spring остается высокомодульным, что позволяет вам выбирать предпочтительный стек, сохраняя при этом слабую связь вашего кода с конкретными реализациями.
Между тем, большинство серверных приложений на JavaScript полагаются на устаревший Express.js, а также на множество специальных промежуточных программ для различных вариантов использования.
Тем не менее, JavaScript прошел долгий путь с тех пор, когда он представлял собой просто набор функций и прототипов, склеенных вместе. Теперь с классами, модулями, потоковыми API, интерфейсами, инкапсуляцией , каждая новая версия JavaScript и TypeScript приобретает высокоуровневые функции, приближающие их к более сложным языкам, таким как Java.
Однако я по-прежнему считаю, что JavaScript не хватает одной из самых выдающихся особенностей Java: Аспектно-ориентированного программирования. Без AOPбыли бы невозможны такие платформы и библиотеки, как Spring, Hibernate и JUnit.
Если вы еще не знакомы с AOP, вот полная информация:
АОП — это парадигма программирования, очень похожая на Объектно-ориентированное программирование.
Парадигма программирования — это совокупность принципов, концепций проектирования и синтаксиса языка, определяющих организацию кода.
Почему объектно-ориентированного программирования недостаточно?
Объектно-ориентированное программирование (ООП) позволяет разработчикам организовывать код в классы, методы и атрибуты для написания более качественного кода.
Определение того, что представляет собой «лучший код», может быть весьма субъективным. Для целей этой статьи давайте рассматривать «лучший код» как код, который остается поддерживаемым с течением времени и может легко развиваться.
Обычно это предполагает стремление к слабой связи, высокой связности, устранению дублирования кода и соблюдению принципов SOLID. сильный>
SOLID: Единаяединая ответственность — принцип Открытьзакрыть/открыть — LЗамена — IРазделение интерфейса — Инверсия зависимостей друг от друга (википедия)
Например, давайте рассмотрим этот код для разработки автоматизированной кофемашины со встроенной кофемолкой.
На первый взгляд этот класс может показаться удобным в использовании, поскольку он определяет множество функций в одном месте. Однако с точки зрения разработчика класс делает слишком много вещей, что делает его разработку и обслуживание сложным, трудоемким и нерентабельным.
Подводя итог, это решение имеет:
- ✅ Ослабленная связь.
- ❌Множество обязанностей
- ❌ Плохая расширяемость
Чтобы решить эти проблемы, ООП продвигает такие принципы проектирования, как инкапсуляция, наследование и полиморфизм, которые помогают разделить код. на более мелкие, более управляемые части.
В нашем примере мы могли бы создать четыре отдельных класса для Grinder, Boiler, Tamper и Steam Wand.
Наша кофемашина могла бы использовать композицию, чтобы использовать их все:
Имея один класс для каждой функции, мы достигаем улучшенного разделения задач, что приводит к повышению удобства сопровождения кода.
В этом сценарии мы достигаем:
- ✅ Единая ответственность
- ✅ Расширяемый
- ❌ Тесная связь — компоненты сильно зависят друг от друга.
Однако все же есть некоторые крайние случаи, когда ООП не может оказать существенную помощь: мы называем их сквозными проблемами.
Сквозные проблемы — это аспекты программного обеспечения, такие как ведение журнала или безопасность, которые влияют на несколько частей программы и не могут быть ограничены одной связной частью системы.
В нашем примере предположим, что кофемашина должна выдавать ошибку, когда резервуар для кофейных зерен, резервуар для воды или резервуар для молока пуст. В случае ошибки должен загореться красный светодиод. Этой проверкой можно управлять с помощью блока try-catch :
Теперь предположим, что мы хотим реализовать второй рецепт кофе с одним методом для каждого рецепта. Самый простой и понятный способ обработки ошибок в обоих методах — многократное копирование и вставка одного и того же блока try-catch, что приводит к образованию большого количества шаблонного кода:
В настоящее время наша кофемашина имеет свои сильные стороны, но также и множество недостатков:
- ✅ Хорошее разделение задач
- ❌ Плотная связь
- ❌ Скопировать шаблонный код.
АОП спешит на помощь.
Один из способов избежать дублирования кода в Java — использовать AOP. Для этого код, связанный со сквозными проблемами, помещается в класс, называемый «Aspect». Затем его можно вызвать из нашего кода, просто добавив аннотацию над классом, методом, атрибутом или параметром.
Например, мы могли бы создать аспект, который управляет ошибкой за нас.
Затем мы могли бы использовать этот аспект там, где это необходимо, помимо методов для наших рецептов кофе, с помощью простой аннотации.
Этот аспект позволяет нам отделить код управления ошибками от кода приготовления кофе. Более того, позже мы сможем изменить обработку ошибок, не касаясь кода кофемашины.
Благодаря этой доработке наша кофемашина стала лучше спроектирована и включает в себя:
- ✅ Улучшенное разделение задач
- ✅ Возможность Plug-and-Play
- ✅ Ослабленная связь.
- ✅ DRY (Don’t повторять себя) — больше не копипастить
Хотя в нашем примере используется TypeScript, реальное применение AOP в современных проектах встречается относительно редко.
Javascript — это не Java.
В Java AOP в значительной степени полагается на такие функции, как путь к классам, который обеспечивает уникальность класса внутри пакета, и надежный API отражения. . Это возможности, которых нет в JavaScript.
Можно возразить, что JavaScript не был разработан для замены Java и не идеально подходит для разработки сложных приложений корпоративного уровня, разработанных с использованием принципов высокого уровня. Однако растущая популярность таких фреймворков, как TypeORM, Angular или Nest.js, доказывает, что я ошибаюсь.
Все эти библиотеки имеют общее свойство использовать декораторы ES для улучшения кода с помощью псевдо AOP.
Хотя этот подход хорошо работает по своему прямому назначению, у него есть заметный недостаток: декораторы ES не являются аннотациями, они являются декораторами. Проще говоря, они следуют шаблону декоратора.
Шаблон «Декоратор» — это шаблон проектирования, который «обертывает» существующие методы, классы, свойства или параметры. Это позволяет вам улучшить функциональность объекта во время выполнения, динамически добавляя дополнительные функции или обязанности.
Напротив, аннотация полагается на какой-то сторонний процесс, чтобы что-то сделать, тогда как декоратор ES — это функция, поведение которой жестко запрограммировано.
При этом декоратор предназначен для одной цели, и его невозможно перепрофилировать для чего-то другого.
Введение в AspectJS
Я твердо уверен, что в экосистеме JavaScript отсутствуют платформы, построенные на основе стандартных аннотаций и обеспечивающие плавную интеграцию, подобно тому, чего Java достигает с помощью таких спецификаций, как JPA, JTA или JaxRS
Помня об этом, я создал AspectJS, библиотеку JavaScript, которая вводит концепцию аннотаций в JavaScript. Проще говоря, аннотация — это пустой декоратор, который зависит от ткача, добавляющего функциональность усиления путем включения тщательно выбранных аспектов.
AspectJS позволяет легко создавать собственные аспекты или аннотации. Однако настоящее преимущество заключается в возможности объединения нескольких аспектов и аннотаций из других библиотек, как только они также примут AspectJS.
На данный момент AspectJS — относительно новый проект, который еще не принят. Вполне возможно, что он так и останется неиспользованным, и сообщество Javascript не осознает необходимости в этом типе инструмента. С другой стороны, это могло быть недостающим элементом, позволяющим кому-то создать Springфреймворк для Javascript.
Мне бы хотелось услышать ваши мысли в комментариях.
Если эта статья пробудила ваше любопытство, попробуйте AspectJS и поделитесь своими отзывами.
Стеккадемический
Спасибо, что дочитали до конца. Прежде чем уйти:
- Пожалуйста, рассмотрите возможность аплодировать и следовать автору! 👏
- Следуйте за нами в Twitter(X), LinkedIn и YouTube.
- Посетите Stackademic.com, чтобы узнать больше о том, как мы демократизируем бесплатное образование в области программирования во всем мире.