Консольное C++ приложение, моделирующее работу компьютерного клуба в течение одного дня. Программа принимает конфигурацию клуба и поток событий, валидирует их, применяет бизнес-логику и выводит полный журнал событий и итоговое состояние столов.
Проект выполнен в учебных целях с акцентом на архитектуру, корректность состояний и расширяемость
- симуляция работы клуба во времени (от открытия до закрытия)
- строгая валидация входных данных
- детерминированная бизнес-логика без скрытых побочных эффектов
- автоматическая генерация системных событий
- расчет выручки и времени занятости столов
Проект логически разделён на четыре уровня:
Parser— проверяет и парсит входные данные (формат и синтаксис). Не содержит бизнес-логики.ClubState— простое хранилище состояния клуба (столы, очередь, клиенты и т.д.). Не содержит правил предметной области; защищает только от низкоуровневых некорректных обращений (out-of-range, отсутствие сущности). Детерминирован и легко тестируется.Events— реализация бизнес-логики. Каждое событие проверяет сценарий (например, свободен ли стол) и при успешной валидации применяет изменения кClubState. Ошибочные сценарии фиксируются какEventError.Run-day— оркестратор работы дня: последовательно применяет входные и автоматически сгенерированные системные события кClubState, собирает итоговый журнал событий и при необходимости форсирует завершение дня.
Преимущества такого разделения:
- чёткое разделение ответственности: парсинг / состояние / правила / исполнение
- добавление нового события — это добавление нового типа, без модификации общей иерархии
- расширение событий возможно без изменения кода класса клуба благодаря механизму
apply_to_club - упрощённое модульное тестирование: состояние и бизнес-правила проверяются отдельно
Стоит понимать:
- класс клуба не защищает от прямого вызова сеттеров вне бизнес-логики; любые нарушения правил должны проверяться через события
Краткий поток исполнения:
Parser → создаёт событие → Run-day вызывает Event::apply_to_club → событие валидирует сценарий и обновляет ClubState.
События реализованы через std::variant, а не иерархию с виртуальными методами:
- нет vtable и косвенных вызовов
- все типы событий известны на этапе компиляции
- обработка событий выражается через
std::visit - добавление нового события — это добавление нового типа, а не изменение базового класса
BaseEvent используется только как носитель общих данных (время, id), а не как полиморфный интерфейс
- Чтение конфигурации клуба
- Парсинг и валидация входных событий
- Последовательное применение событий и генерация системных событий
- Принудительное завершение дня в случае ошибки
- Печать итогового состояния
EventClientWaitпо ТЗ не обрабатываетClientUnknown, из-за чего он сразу упадет с ошибкой. Так же не сказано, что будет, если клиент уже сидит или если клиент уже в очереди.EventClientLeftаналогично по ТЗ не сказано, что будет, если клиент стоит в середине очереди. Гарантируется ли тот факт, что вставшие в очередь клиенты будут стоять до закрытия, ожидая свободных столиков?
Данный неточности были проигнорированы при реализации, что является потенциальной уязвимостью: можно сломать инварианты
- C++20
- CMake ≥ 3.19
- Стандартная библиотека без внешних зависимостей
- GTest (Linux: libgtest-dev, Windows: vcpkg или MSYS2)
CI сборка:
- Linux: GCC, CMake + Ninja/Make
- Windows MinGW: GCC из MSYS2, сборка через Ninja
- Windows MSVC: Visual Studio 17 2022, зависимости через vcpkg
Сборка из корня репозитория:
cmake -S . -B build -DCMAKE_BUILD_TYPE=Releasecmake --build buildЗапуск из корня репозитория с входным файлом input.txt
./build/test-app input.txtПроект использует GoogleTest и интегрирован с CMake
Тесты собираются как отдельные исполняемые файлы и не включаются в сборку основного приложения по умолчанию, а так же:
- Компиляторные флаги строго платформенные (GCC/Clang: -Wall -Wextra -Wpedantic -Werror, MSVC: /W4 /WX /permissive-)
- Собираются с динамическими санитайзерами
-fsanitize=address,undefined - Собираются после проверки статическим санитайзером clang-tidy
Для каждого логического модуля определен отдельный тестовый таргет:
parser-testclub-testevents-testrun-day-test
Тесты запускаются через CTest с цветным выводом (GTEST_COLOR=1):
cmake -S . -B buildcmake --build build --target execute-tests