JavaRush /Java блог /Java-проекты /Рефакторим и ретроспективим - "Java-проект от А до Я"
Roman Beekeeper
35 уровень

Рефакторим и ретроспективим - "Java-проект от А до Я"

Статья из группы Java-проекты
Всем привет, дорогие друзья. Это последняя статья в нашей серии по созданию Java-проекта от А до Я. Сегодня поговорим о том, что хорошо бы отрефакторить, а также обсудим результат нашей работы и поговорим о планах на будущее проекта JavaRushTelegramBot. "Java-проект от А до Я": Рефакторим и ретроспективим - 1

Отрефакторим наш код

Создадим под это дело новую ветку: STEP_10, в которую запишем все последние изменения до MVP. Рефакторинг — это изменение кодовой базы без изменения ее поведения. Что хочется изменить? В самом начале термином “статья” на проекте я выбрал слово article и пока не начал работать с клиентом Javarush API для статей, это было логично. Но в Javarush API используют термин post. И я думаю, что хорошо будет перейти на их стандарт, поэтому первым делом переименуем везде с article на post. Что будет нашей гарантией, что все осталось также? Гарантией будут наши тесты, которые мы написали. Именно теперь они нам понадобятся. Начнем изменения с БД. В таблице group_sub, нужно переименовать поле lastArticleId в lastPostId. Это очень просто, потому что у нас уже настроен Flyway и для того, чтобы обновить поле, нужно всего добавить еще одну миграцию, в которой мы выполним ALTER TABLE операцию. Создадим новую, третью, миграцию: V00003__rename_last_article_id.sql в котором будет:

ALTER TABLE group_sub CHANGE COLUMN last_article_id last_post_id INT;
Эту миграцию нужно поместить в тоже место, где лежат и две предыдущие. Далее нужно переименовать имя переменной в сущности GroupSub с lastArticleId на lastPostId. Если работа идет через IDEA, то провести переименование очень просто, нужно воспользоваться горячей клавишей: shift + F6, когда курсор находится на имени класса/метода/поля, тогда среда разработки позволяет переименовать все места, где используется это поле. Также переименует геттеры и сеттеры для этого поля.Это полезная вещь и должна быть в арсенале каждого разработчика. Я уверен, что такой же функционал есть и у эклипса, но подсказать не могу, я им не пользуюсь."Java-проект от А до Я": Рефакторим и ретроспективим - 2поэтому переименуем

@Column(name = "last_article_id")
private Integer lastArticleId;
в

@Column(name = "last_post_id")
private Integer lastPostId;
Далее при помощи поиска по всему проекту (ctrl+shift+f) найдем все места, где есть упоминания Article:"Java-проект от А до Я": Рефакторим и ретроспективим - 3Видно из рисунка, что у нас еще есть имена классов и методов со словом Article. Их тоже нужно переименовать в Post. Используем уже известную команду shift+F6 для них:
  • класс FindNewArticlesJob -> FindNewPostsJob

  • класс FindNewArticleService -> FindNewPostsService

  • метод FindNewArticleService#findNewArticles -> FindNewPostsService#findNewPosts

  • класс FindNewArticleServiceImpl -> FindNewPostsServiceImpl

  • проперти bot.recountNewArticleFixedRate -> bot.recountNewPostFixedRate

  • метод JavaRushGroupClient#findLastArticleId -> JavaRushGroupClient#findLastPostId

  • метод FindNewPostsJob#findNewArticles -> FindNewPostsJob#findNewPosts

  • И остальные места, где внутренние методы, где комментарии.

Также у нас еще есть BPMN модель, в которой описан процесс поиска новых статей. Его менять мы будем через специальную программу CamundaModeler, через нее и формирую новую картинку в README. В результате получим:"Java-проект от А до Я": Рефакторим и ретроспективим - 4Еще хочу привести к одному виду поиск чего-то. На данный момент у меня используется два глагола: find и retrieve. Хочу переименовать все retrieve в find:
  • метод CommandContainer#retrieveCommand -> CommandContainer#findCommand
  • все джавадоки с retrieve в find.
Далее хочется решить поле chatId. Опираясь на доку из телеграма, можем сказать, что это числовое значение. Поэтому я думаю будет хорошим тоном сделать также и в приложении. В базе данных оно стоит как строковое значение. Сделали мы это потому, что по какой-то причине в нашей библиотеке работы с ботом при отправке сообщений chatId был именно строковым. Поменяем все так, чтобы конвертировать в строку именно в тот момент. Первым делом обновляем интерфейс SendBotMessageService:

/**
* Service for sending messages via telegram-bot.
*/
public interface SendBotMessageService {

   /**
    * Send message via telegram bot.
    *
    * @param chatId  provided chatId in which would be sent.
    * @param message provided message to be sent.
    */
   void sendMessage(Long chatId, String message);

   /**
    * Send messages via telegram bot.
    *
    * @param chatId  provided chatId in which would be sent.
    * @param message collection of provided messages to be sent.
    */
   void sendMessage(Long chatId, List<String> message);
}
Теперь принимает интерфейс не String chatId, а Long chatId. Обновляем и реализацию, ставим просто chatId.toString() в необходимом поле:

sendMessage.setChatId(chatId.toString());
Далее идем в класс TelegramUser и меняем тип переменной chatId с String на Long:

@Id
@Column(name = "chat_id")
private Long chatId;
В TelegramUserRepostiry меняем в наследуемом интерфейсе также со String на Long:

/**
* {@link Repository} for handling with {@link TelegramUser} entity.
*/
@Repository
public interface TelegramUserRepository extends JpaRepository<TelegramUser, Long> {
   List<TelegramUser> findAllByActiveTrue();
   List<TelegramUser> findAllByActiveFalse();
}
Обновляем метод CommandUtils#getChatId, чтобы он не приводил к строке:

/**
* Get chatId from {@link Update} object.
*
* @param update provided {@link Update}
* @return chatID from the provided {@link Update} object.
*/
public static Long getChatId(Update update) {
   return update.getMessage().getChatId();
}
И во всех местах, где используется chatId, убираем приведение к строке. Там, где методы принимают String chatId, меняем тип на Long. Кстати, пока менял приведение к строке нашел, что утилитный метод getChatId(update) не везде используется, так что обновил и те места. Последний этап — нужно обновить тип поля в таблице tg_user. Для этого создадим четвертую миграцию V00004_change_chat_id_type_to_long.sql:

ALTER TABLE tg_user MODIFY chat_id INT;
После всех этих обновлений пришло время запустить тесты :D В результате упал только один тест, могло быть и хуже. Оказалось, что падал тест из-за того, что JUnit позволяет компилировать сравнение разных типов при assert:"Java-проект от А до Я": Рефакторим и ретроспективим - 5Как только я вместо String.valueOf(i + 1) сделал Long.valueOf(i + 1), то тест начал работать, как ожидается. Теперь нужно удостовериться, что бот запускается и работает, как ожидается. Попробую добавить подписку, посмотреть список подписок и удалить одну из них. Думаю, этого хватит для того, чтобы проверить на smoke test. Поклацал все, что мог, все работает. Это хорошо, значит, рефакторинг прошел именно так, как я хотел, — изменился код, но изменения никак не повлияли на поведение бота. Мы провели неплохой рефакторинг по проекту. Привели к общему знаменателю термины и убрали путаницу с типом переменной chatId.

Последние приготовления перед выходом в свет стабильной версии

Теперь, когда уже написан весь код, который планировался, можно сделать последние приготовления. Обновим версию в приложении на стабильную 1.0.0:

<version>1.0.0</version>
и добавим описание в RELEASE_NOTES:
# Release Notes ## 1.0.0 Implemented all the logic, planned up to MVP: * User can subscribe on group of posts * User can view list of group subscriptions on which user subscribes * User can unsubscribe from group of posts * User can set an inactive bot and do not receive notifications * User can restart getting notifications * Admin has ability to see bot statistics
Все эти пункты я взял из README.md, думаю они как раз опишут результат работы, которая была проделана за эти 8 месяцев. Следующий этап — создаем коммит, в нем я дублирую данные из RELEASE_NOTES. Получим ПР с последними изменениями: STEP_10: v1.0.0.

Создаем наш первый релиз: v1.0

Весь код, который мы планировали уместить в MVP, готов, и теперь нужно подвести черту — создать релиз. Что такое релиз? Это опять калька из английского слова release, которое в нашем случае означает выход новой версии продукта. Для этого у GitHub в репозитории есть секция Releases, в которой как раз будут храниться релизы. Только их нужно создавать."Java-проект от А до Я": Рефакторим и ретроспективим - 6Смержим последний пулл-реквест и создадим свой первый релиз на основе последнего коммита в main ветке. Переходим и создаем новый релиз и заполняем данными:"Java-проект от А до Я": Рефакторим и ретроспективим - 7Все, можно нажимать Publish Release. И все, теперь у нас есть наш первый релиз приложения. Пришло время и для ретроспективы.

Что дальше с ботом?

Конечно, хочется, чтобы бот начал развиваться за счет сообщества. Чтобы молодые разработчики предлагали свои идеи по реализации новой функциональности. Но здесь насильно мил не будешь, пока таких нет, и я буду посильно развивать его. Есть несколько направлений, которые интересны для меня:
  • Создать джобу для ежедневного сбора и сохранения статистики бота, чтобы можно было отследить во времени его развитие.

  • Настроить работу с бэкапами, в целом идея описана в предыдущей статье.

  • Подумать над унитаризацией подхода в создании телеграм бота для получения уведомлений о новых статьях. В идеале (хотя я не знаю, получится ли такое) сделать так, чтобы был опубликованный Spring boot стартер, который бы требовал настройки базы данных, бота и реализации интерфейсов работы для получения данных о статьях, а остальное бы делала эта библиотека. Посмотрим что из этого может получиться, тут нужно хорошенько подумать.

  • Расширить статистику для всех по авторам: сколько статей, количество просмотров статей, какая оценка статей. По понятным причинам JavaRush не является блогом для написания статей, и поэтому такого функционала у него попросту нет. А мне, как автору, это интересно. При помощи бота можно создать продуманную статистику и вести ее.

  • Реализовать обработку исключений так, чтобы было пользователю было понятно, что произошло.

  • Выделить отдельно Java-библиотеку с JavaRush API client.

  • Добавить GitHub action, который бы создавал релиз автоматически каждый раз, когда обновляется ветка main.

  • Добавить подписку на конкретного автора, а не на группу статей.

  • Улучшить UI представление бота. Вместо уродливых команд создать красивые кнопки, которые бы помогли более приятно и элегантно управлять ботом.

  • Так как информация о новых статьях может быть интересна еще каким-то каналам (как минимум у меня есть такой интерес к моим статьям и моему телеграм-каналу), то хорошо бы добавить возможность боту отправлять сообщения о новых статьях напрямую в какой-то канал, а не только в личных сообщениях.

  • Изучить опыт ботов, которые работают в группах и посмотреть, что можно сделать с этим в нашем случае. Я уверен, что есть закрытые группы людей, изучающих Java, которым было бы интересно получать уведомления о новых статьях.

Это не все идеи, которые были у меня во время работы над проектом, а те, которые я успел записать, пока они не улетучились)) Если есть идеи, пишите в комментариях, я с удовольствием поговорю о них.

Ретроспектива (вместо окончания)

Я часто повторяю это слово уже последние несколько статей. Что это значит? Для меня в рамках этой серии — это посмотреть на то, что я хотел сделать и что за эти 8 месяцев получилось. Было немало комментариев на тему того, что все это классно, хоть бы автор сделал это до конца:"Java-проект от А до Я": Рефакторим и ретроспективим - 8"Java-проект от А до Я": Рефакторим и ретроспективим - 9"Java-проект от А до Я": Рефакторим и ретроспективим - 10Теперь уже можно сказать, что да, я таки завершил. Думал ли я, что это будет тааак долго? Нет, не думал. Ожидал, что сделаю до начала 2021 года. Но я понял, что нужно рассказать и о базах данных, которые вышли на целую серию статей, и о многих технологиях. И это растянулось. Хотя могу сказать, что и результатом и объемом работы доволен. Я вижу людей, которые учатся на этом, выражают свою благодарность, и это мотивирует меня идти дальше. Что это дало мне? Прежде всего понимание, что такие проекты можно брать и они пользуются интересом. Я всегда с большим удовольствием читаю комментарии к статьями и в меру сил отвечаю на них. Конечно, я стал опытнее и в написании статей, и в написании приложений, потому что процесс развертывания приложения потребовал от меня усовершенствование моих навыков. Да, я зналб что я хочу сделать в итоге, я зналб что это реально. И все равно потребовалось время на изучение реализации моих идей. В процессе написания этой серии захотелось как-то систематизировать свои материалы в одном местеб и для этого я создал свой телеграм-канал, в котором уже более 400 подписчиков. Для меня это шикарный результат. Новое направление развития. Теперь есть понимание, куда расти дальше. Также в процессе родился новый продукт, которым уже пользуются люди и я в частности — Javarush Telegram Bot. Да, пока что только 26 активных пользователей. Но я верю, что это будет востребовано и количество пользователей будет расти. Чем хорош этот проект? Его еще развивать и развивать. Студенты из JavaRush, которые хотят отточить свои навыки, смогли бы предлагать новый функционал и реализовывать его. А я буду руководить этим проектом и проводить Code Review всех изменений. Финализируем то, что планировалось и что получилось:
  • Главной целью проекта было написание приложение с базой данных, в котором была бы настроена работа по развертыванию и управлению. Эта цель точно достигнута.

  • Сроки разработки сдвинулись и были проблемы с выходом статей. Были моменты, когда не было статей по 2-3 недели.

  • Полностью настроили работу с БД, добавили Flyway.

  • Описали работу с Maven в нескольких статьях.

  • Поговорили о Docker. Не так много и детально, как хотелось бы, но все же.

  • Баш скрипты мы тоже затронули, у нас на этом настроен запуск всего развертывания.

  • По планированию проекта можно сказать, что все прошло максимально реалистично. Только несколько задач были добавлены в процессе написания.

Что не сделали?
  • Полностью не рассмотрели сравнение двух решений — Flyway и Liquibase.

  • Как-то вскользь поговорили о Lombok’e. Так только на практике и все. Хотелось бы больше.

  • Мало уделили время для UniRest решения.

Может, я что-то упустил? Пишите в комментариях. Спасибо всем, кто активно участвовал в процессе развития этого проекта. Жду от вас отзыва о том, что в итоге получилось. Обратная связь, так сказать) Пока я писал эту серию, у меня накопилось много интересных тем, о которых я бы хотел поговорить, так что скоро будем писать свое резюме на GitHub! До встречи)

Список всех материалов серии в начале этой статьи.

Комментарии (21)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Bolivar Li Уровень 32
28 декабря 2023
Уффф дошел, спасио Роман. Было очень интересно. Сформировалось понимание куда дальше копать , теже тесты )) 🤝🤝🤝
kiiril Уровень 51
16 декабря 2023
Отличный набор статей, я как человек на финишной прямой своего обучения был рад сделать этот проект, разобраться в чем-то новом и подчеркнуть для себя какие-то интересные подходы. Спасибо автору за проделанную работу и думаю еще не один ученик JavaRush будет делать этот проект)
Kirill Уровень 32
11 ноября 2022
Добрался! :D Роман, по пути уже выражал благодарность, но хочу еще раз сказать, спасибо за проделанный труд! Иногда было потно и слезливо, а иногда было задорно и радостно:) В общем и целом могу сказать что предоставленная информация была очень полезной и помогла сложить хоть какое-то представление о написании приложений!
Alexander Lipatov Уровень 1
31 октября 2022
Роман, спасибо огромное за данный цикл статей! Местами было сложно, тк по сути мое первое большое приложение. Реализовал без докера. Но все равно очень рад! Было море полезной информации. Успехов и удачи!
Kosmos Уровень 35
24 мая 2022
Ура!!! Я это сделал - релиз готов. Кто молодец - я молодец!!! 🥳 Спасибо тебе Роман за твои труды.
Рустам Косимов Уровень 31
1 января 2022
Спасибо, просто БРАВО!!! Узнал очень много чего нового, теперь хоть есть какое-то понимание как делать приложения
Hamza Уровень 0
4 октября 2021
Спасибо большое за проделанный труд, давно искал такого рода обучалки. Успехов Вам!
17 июня 2021
Респект тебе и уважуха за твои старания! Такой выносливости и целеустремленности можно только позавидовать. Предлагаю следующий проект - операционную систему на java😀
Сергей Цыманов Уровень 35
9 июня 2021
Добрый день, скрипт который апдейтит колонку chat_id в INT не срабатывает потомучто на ней висит foreign key, ошибка при деплое. Нужно сначала сбросить ключи, потом обновить и затем обратно повесить
Dmitry Nevar Уровень 26
8 июня 2021
Роман! У меня еще есть вопрос, как мне сделать бот рабочим вне idea, чтоб я мог добавлять в него функционал и показывать как рабочий проект в портфолио! а возможно и переделал его под нужды друзей/родных и пользовал как полезный инструмент)