/* * api_client.h — HTTP-клиент, реализующий паттерн «Синглтон» * * ╔══════════════════════════════════════════════════════════════════╗ * ║ ПАТТЕРН «СИНГЛТОН» (Singleton) ║ * ╠══════════════════════════════════════════════════════════════════╣ * ║ Задача: гарантировать, что в приложении существует ровно ║ * ║ ОДИН экземпляр QNetworkAccessManager. Создание нескольких ║ * ║ менеджеров приводит к дублированию соединений и cookie-хранилищ.║ * ║ ║ * ║ Реализация (Meyers' Singleton): ║ * ║ • статическая локальная переменная в instance() ║ * ║ • конструктор объявлен private ║ * ║ • копирующий конструктор и operator= удалены (= delete) ║ * ╚══════════════════════════════════════════════════════════════════╝ * * ApiClient также является «клиентом» паттерна «Адаптер»: * он хранит указатель на IQuizAdapter и использует его * для преобразования ответов сервера в Quiz-объекты, * не зная о деталях реализации парсинга. * * Коммуникация с вызывающим кодом — через механизм сигналов Qt: * quizzesReceived — список тестов успешно получен и десериализован * quizReceived — один тест успешно получен и десериализован * quizCreated — тест создан (POST) * quizUpdated — тест обновлён (PUT) * quizDeleted — тест удалён (DELETE) * errorOccurred — произошла сетевая ошибка или ошибка парсинга */ #pragma once #include // базовый класс Qt с поддержкой сигналов/слотов #include // выполняет HTTP-запросы асинхронно #include // тело запросов POST/PUT #include // контейнер для списка тестов #include "quiz.h" #include "iquiz_adapter.h" class ApiClient : public QObject { /* * Q_OBJECT — макрос Qt, активирует Meta-Object System: * систему сигналов/слотов, rtti, tr() и пр. * Обязателен для любого класса, использующего signals/slots. */ Q_OBJECT public: /* * Единственная точка доступа к экземпляру (Singleton Access Point). * Возвращает ссылку (не указатель), чтобы исключить проверку на nullptr. */ static ApiClient &instance(); // GET /api/quiz/ — получить все тесты void fetchQuizzes(); // GET /api/quiz/:id/ — получить один тест по ID void fetchQuiz(int id); // POST /api/quiz/ — создать тест; тело запроса передаётся как QJsonObject void createQuiz(const QJsonObject &data); // PUT /api/quiz/:id/ — полностью обновить тест по ID void updateQuiz(int id, const QJsonObject &data); // DELETE /api/quiz/:id/ — удалить тест по ID void deleteQuiz(int id); signals: void quizzesReceived(QList quizzes); void quizReceived(Quiz quiz); void quizCreated(Quiz quiz); void quizUpdated(Quiz quiz); void quizDeleted(QString message); void errorOccurred(QString error); private: explicit ApiClient(QObject *parent = nullptr); ~ApiClient() override; ApiClient(const ApiClient &) = delete; ApiClient &operator=(const ApiClient &) = delete; QNetworkAccessManager *m_manager; IQuizAdapter *m_adapter; QString m_baseUrl; };