Табло теннисного матча

Проект “Табло теннисного матча” #

Веб-приложение, реализующее табло счёта теннисного матча.

Комментарии по проекту - https://www.youtube.com/watch?v=zAOiNa24jpg.

Что нужно знать #

Мотивация проекта #

  • Создать клиент-серверное приложение с веб-интерфейсом
  • Получить практический опыт работы с ORM Hibernate
  • Сверстать простой веб-интерфейс без сторонних библиотек
  • Познакомиться с архитектурным паттерном MVC(S)

Комментарии:

  • Проект не подразумевает фреймворки, ради практики с паттерном MVC, Spring Boot начнется с проекта #6
  • Не используем Bootstrap, для практики верстки вручную, Bootstrap можно будет использовать в проекте #5
  • Проект не многопользовательский, поэтому не используем сессии

Функционал приложения #

Работа с матчами:

  • Создание нового матча
  • Просмотр законченных матчей, поиск матчей по именам игроков
  • Подсчёт очков в текущем матче

Подсчёт очков в теннисном матче #

В теннисе особая система подсчёта очков - https://www.gotennis.ru/read/world_of_tennis/pravila.html

Для упрощения, допустим что каждый матч играется по следующим правилам:

  • Матч играется до двух сетов (best of 3)
  • При счёте 6/6 в сете, играется тай-брейк до 7 очков

Интерфейс приложения #

Главная страница #

  • Ссылки, ведущие на страницы нового матча и списка завершенных матчей

Страница нового матча #

Адрес - /new-match.

Интерфейс:

  • HTML форма с полями “Имя игрока 1”, “Имя игрока 2” и кнопкой “начать”. Для упрощения допустим, что имена игроков уникальны. Игрок не может играть сам с собой.
  • Нажатие кнопки “начать” приводить к POST запросу по адресу /new-match

Обработчик POST запроса:

  • Проверяет существование игроков в таблице Players. Если игрока с таким именем не существует, создаём
  • Создаём экземпляр класса Match (содержащий айди игроков и текущий счёт) и кладём в коллекцию текущих матчей (существующую только в памяти приложения, либо в key-value storage). Ключом коллекции является UUID, значением - экземпляр класса Match
  • Редирект на страницу /match-score?uuid=$match_id

Страница счёта матча - /match-score #

Адрес - /match-score?uuid=$match_id. GET параметр uuid содержит UUID матча.

Интерфейс:

  • Таблица с именами игроков, текущим счётом
  • Формы и кнопки для действий - “игрок 1 выиграл текущее очко”, “игрок 2 выиграл текущее очко”
  • Нажатие кнопок приводит к POST запросу по адресу /match-score?uuid=$match_id, в полях отправленной формы содержится айди выигравшего очко игрока

Обработчик POST запроса:

  • Извлекает из коллекции экземпляр класса Match
  • В соответствии с тем, какой игрок выиграл очко, обновляет счёт матча
  • Если матч не закончился - рендерится таблица счёта матча с кнопками, описанными выше
  • Если матч закончился:
    • Удаляем матч из коллекции текущих матчей
    • Записываем законченный матч в SQL базу данных
    • Рендерим финальный счёт

Страница сыгранных матчей - /matches #

Адрес - /matches?page=$page_number&filter_by_player_name=$player_name. GET параметры:

  • page - номер страницы. Если параметр не задан, подразумевается первая страница
  • filter_by_player_name - имя игрока, матчи которого ищем. Если параметр не задан, отображаются все матчи

Постранично отображает список сыгранных матчей. Позволяет искать матчи игрока по его имени. Для постраничного отображения потребуется реализация пагинации.

Интерфейс:

  • Форма с фильтром по имени игрока. Поле ввода для имени и кнопка “искать”. По нажатию формируется GET запрос вида /matches?filter_by_player_name=${NAME}
  • Список найденных матчей
  • Переключатель страниц, если матчей найдено больше, чем влезает на одну страницу

База данных #

В качестве базы данных предлагаю использовать H2. Это in-memory SQL база для Java. In-memory означает то, что движок БД и сами таблицы существуют только внутри памяти Java приложения. При использовании in-memory хранилища необходимо инициализировать таблицы базы данных при каждом старте приложения.

Таблица Players - игроки #

Имя колонкиТипКомментарий
IDIntПервичный ключ, автоинкремент
NameVarcharИмя игрока

Индексы:

  • Уникальный индекс колонки Name для эффективности поиска игроков по имени и запрета повторяющихся имён

Таблица Matches - завершенные матчи #

Для упрощения, в БД сохраняются только доигранные матчи в момент их завершения.

Имя колонкиТипКомментарий
IDIntПервичный ключ, автоинкремент
Player1IntАйди первого игрока, внешний ключ на Players.ID
Player2IntАйди второго игрока, внешний ключ на Players.ID
WinnerIntАйди победителя, внешний ключ на Players.ID

MVCS #

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

Учёт счёта матча #

Пример (именование классов и сервисов на мой вкус):

MatchScoreController:

  • Обрабатывает POST запросы к /match-score
  • Через OngoingMatchesService получает экземпляр класса Match для текущего матча, который является моделью/частью модели MatchScoreModel
  • Через MatchScoreCalculationService обновляет счёт в матче
  • Если матч закончился - через FinishedMatchesPersistenceService сохраняет законченный матч в базу данных
  • С помощью MatchScoreView отображает MatchScoreModel в виде отрендеренного HTML

Каждый из упомянутых сервисов делает конкретную работу:

  • OngoingMatchesService хранит текущие матчи и позволяет их записывать/читать
  • MatchScoreCalculationService реализует логику подсчёта счёта матча по очкам/геймам/сетам
  • FinishedMatchesPersistenceService инкапсулирует чтение и запись законченных матчей в БД

Тесты #

Покроем юнит тестами подсчёт очков в матче. Примеры кейсов:

  • Если игрок 1 выигрывает очко при счёте 40-40, гейм не заканчивается
  • Если игрок 1 выигрывает очко при счёте 40-0, то он выигрывает и гейм
  • При счёте 6-6 начинается тайбрейк вместо обычного гейма

Предлагаю студентам самостоятельно придумать тест кейсы для покрытия всех вариантов изменения счёта в матче, особенно правила “больше-меньше” и тайбрейк. Набор тестов должен быть реализован с помощью JUnit 5.

Деплой #

Будем вручную деплоить war артефакт в Tomcat, установленный на удалённом сервере. При использовании базы данных H2, установка внешней SQL БД не требуется.

Шаги:

  • Локально собрать war артефакт приложения
  • В хостинг-провайдере по выбору арендовать облачный сервер на Linux
  • Установить JRE и Tomcat
  • Зайти в админский интерфейс Tomcat, установить собранный war артефакт

Ожидаемый результат - приложение доступно по адресу http://$server_ip:8080/$app_root_path.

План работы над приложением #

  • Классы-модели Hibernate для таблиц БД
  • Страница создания нового матча
  • Сервисы для хранения текущих матчей и подсчета очков в матче, юнит тесты для подсчёта очков
  • Страница счёта матча
  • Сервис для сохранения законченного матча в БД
  • Сервис поиска законченных матчей по имени игрока
  • Страница отображения законченных матчей, поиска матчей по имени игрока
  • Деплой на удалённый сервер

Ресурсы для работы над ошибками #

  • Реализации проекта другими студентами и мои ревью этих реализаций
  • Готовый проект можете отправить мне на ревью - https://t.me/zhukovsd
    • [Обновление от 1 августа 2023] - целевое количество видеоревью этого проекта на Java накоплено, новые реализации к полноценным видеоревью не принимаются (на Python принимаются). В любом случае призываю отправлять законченные проекты в чат, добавляю их в список. Подробности - https://t.me/zhukovsd_it_mentor/57