Известно, что для создания масштабных высокопроизводительных систем сегодня применяются, в основном, облачные вычисления. Все облака базируются на виртуализации, которая в настоящее время тесно связана с ОС и гипервизорами типа
VMware и
Xen. Тем не менее, всем известно, что одна из старых и, тем не менее, популярных технологий виртуализации — Java с ее виртуальной машиной, выполняющей
байт-коды программы. Поэтому сегодня мы решили рассказать об одной из фишек Java.
После того, как в Java 5 были введены
аннотации, все, кто работает с Java, пришли в большое возбуждение. Еще бы — такой отличный инструмент, позволяющий сделать код короче! Больше никаких конфигурационных файлов Hibernate/Spring
XML, больше никаких маркерных интерфейсов! Только аннотации — прямо здесь, в коде, там, где они нужны. Однако, протестировав аннотации Java, вряд ли
кто-то останется в таком же радостном расположении духа. Более того, аннотации начинают казаться большой ошибкой в дизайне Java.
Говоря в двух словах, с аннотациями есть одна большая проблема — они подталкивают нас к тому, чтобы оставлять функционал объекта вне его самого объекта, что нарушает
принцип инкапсуляции.
Объект больше не является цельным, поскольку его поведение не определяется больше его собственными методами —
какая-то часть его функционала остается в другом месте. Почему это плохо? Рассмотрим примеры.
@ Injeсt
Скажем, мы аннотируем
проперти с помощью @ Injeсt:

Теперь у нас есть инжектор, который знает, что инжектировать:

Теперь мы создаем экземпляр класса Books при помощи контейнера:

Класс Books не имеет представления о том, кто и как будет инжектировать экземпляр класса DB. Это произойдет «за кулисами» и вне его контроля. Это сделает инжектирование. Возможно, это и кажется удобным, но такое поведение вызывает значительные повреждения во всей основе кода. Происходит не инверсия управления — управление полностью утрачивается. Объект больше не отвечает за то, что с ним происходит.
А вот как это должно было быть сделано:

Аннотации провоцируют нас на создание и использование контейнеров. Мы выносим функциональность за границы объекта и помещаем ее в контейнеры или
куда-то еще. И это
из-за того, что мы не хотим дублировать код снова и снова, так ведь? И это правильно, дупликация кода — не самое лучшее занятие, но разрывание объекта еще хуже. Это же относится и к ORM (JPA/Hibernate), где аннотации используются достаточно активно.
Аннотации сами по себе не являются мотиваторами, но они поощряют разрывание нами объектов и хранение их частей в разных местах, таких как контейнеры, сессии, контроллеры и так далее.
@XmlElement
Вот как работает JAXB, когда вы хотите конвертировать ваш POJO в XML. Для начала вы присоединяете к геттеру аннотацию @XmlElement:

Затем вы создаете маршалер и просите его сконвертировать экземпляр classBook в XML:

Кто создает XML? Точно не book.
Кто-то другой, вне classBook — и это неверно. Вот как это должно было быть сделано. Начнем с того, что класс не имеет представления об XML:

Затем декоратор выводит его в XML:

Теперь для печати книги в XML мы делаем следующее:

Печатный функционал XML — внутри XmlBook. Если вам не по вкусу идея декоратора, вы можете перенести метод toXML() в класс DefaultBook, это неважно. Важно то, что функционал всегда остается там, где он должен быть — внутри объекта. Только объект знает, как напечатать себя в XML, и никто более.
@RetryOnFailure
Вот еще один пример:

После компиляции мы запускаем так называемый
AOP weaver, который технически преобразует наш код во
что-то вроде этого:

Алгоритм здесь упрощен, но в целом идея ясна. AspectJ, движок
АОП использует аннотацию @RetryOnFailure в качестве сигнала, который информирует нас о том, что класс должен быть обернут в другой. Это также происходит «за кулисами»: мы не видим дополнительный класс, который реализует алгоритм повторной передачи. Но
байт-код, произведенный уивером AspectJ содержит модифицированную версию класса Foo.
Это именно то, что неверно в этом подходе — мы не видим и не можем контролировать создание экземпляра дополнительного объекта. Состав объекта, наиболее важный процесс в дизайне объекта, скрыт от нас. Вы можете сказать, что мы и не должны его видеть, так как он дополнительный. Но ведь мы должны знать, как образованы наши объекты? Нас может не волновать, как они работают, но мы должны видеть весь процесс композиции.
Такой дизайн выглядит гораздо лучше (вместо аннотаций):

Затем внедрение FooThatRetries:

А затем внедрение Retry:

Конечно, такой код длиннее, но он куда чище. Аннотации — плохой метод; вместо них используйте лучше композицию объекта.
Что может быть еще хуже, чем аннотации, так это конфигурации. Например, конфигурации XML. Механизмы конфигурации Spring XML — это отличный пример ужасного дизайна.
Не должно быть никаких «конфигураций» в
ООП. Конфигурировать объекты, если они реальны, невозможно. Мы можем только инстанцировать их, и лучший метод инстанцирования — оператор new. Это ключевой оператор для разработчика ООП. Его игнорирование и использование «механизмов конфигурации» — просто преступление.
комментарии (43)