- Fondements de la programmation objet en C++
La programmation orientée objet (POO) structure le code autour d’entités autonomes possédant état et comportement. Les piliers de ce paradigme – encapsulation, héritage et polymorphisme – facilitent la maintenance et l’évolution du logiciel.
1.1 Encapsulation et abstraction
L’encapsulation consiste à regrouper données et méthodes dans une classe tout en contrôlant leur visibilité (public, protégé, privé). L’abstraction permet de modéliser un concept du monde réel en exposant uniquement les détails pertinents. Par exemple, une classe Chambre peut exposer des méthodes reserver() ou liberer() sans révéler comment l’information est persistée.
1.2 Héritage et polymorphisme
L’héritage autorise la création de classes spécialisées à partir d’une classe de base. Considérons une hiérarchie ServiceSupplementaire avec des sous-classes comme PetitDejeuner et Navette. Le polymorphisme, via des foncctions virtuelles, permet d’appeler calculerMontant() sur un pointeur de la classe de base tout en obtenant un comportement spécifique à chaque service.
class ServiceSupplementaire {
public:
virtual double calculerCout(int duree) const = 0;
virtual ~ServiceSupplementaire() = default;
};
class Navette : public ServiceSupplementaire {
double tarifParTrajet;
public:
Navette(double tarif) : tarifParTrajet(tarif) {}
double calculerCout(int nbTrajets) const override {
return nbTrajets * tarifParTrajet;
}
};
- Gestion des chambres et des réservations
Le module de gestion des chambres assure le suivi en temps réel de l’état de chaque unité et orchestre les opérations de réservation et d’annulation.
2.1 Cycle de vie d’une chambre
Une chambre peut être dans l’un des états suivants : libre, réservée, occupée, en nettoyage, ou en maintenance. La transition entre états est provoquée par des événements métier. L’état est stocké en base de données via une colonne etat dans la table chambres.
-- Mise à jour de l'état après un départ
UPDATE chambres SET etat = 'nettoyage' WHERE id_chambre = 104;
2.2 Logique de réservation et annulation
La fonction effectuerReservation vérifie la disponibilité, crée une entrée dans la table reservations et passe l’état de la chambre à « réservée ». L’annulation remet l’état à « libre » et calcule d’éventuels frais.
void annulerReservation(int idResa) {
QSqlDatabase db = QSqlDatabase::database();
db.transaction();
QSqlQuery query;
query.prepare("UPDATE chambres SET etat = 'libre' WHERE id_chambre = "
"(SELECT id_chambre FROM reservations WHERE id = ?)");
query.addBindValue(idResa);
if (!query.exec()) {
db.rollback();
return;
}
query.prepare("DELETE FROM reservations WHERE id = ?");
query.addBindValue(idResa);
query.exec();
db.commit();
}
- Conception modulaire du système
Le découpage en modules autonomes améliore la lisibilité et la testabilité. Chaque module expose une interface publique minimale et communique avec les autres via des messages ou un accès contrôlé aux données partagées.
3.1 Identification des modules
L’analyse fonctionnelle conduit à isoler les modules suivants : gestion des clients, gestion des chambres, facturation, services additionnels et reporting. Chaque module correspond à un espace de noms distinct dans le code source.
3.2 Communication inter-modules
Au lieu d’appels directs, on utilise un mécanisme de signaux et slots. Par exemple, le module de nettoyage émet un signal chambreNettoyee(int id) auquel le module de réservation est connecté.
connect(nettoyageModule, &Nettoyage::chambreNettoyee,
reservationModule, &Reservations::mettreAJourDisponibilite);
L’accès aux données partagées est synchronisé via des transactions et des verrous optimistes lorsque plusieurs modules écrivent dans la même table.
- Accès aux données avec ODBC et SQL
La couche d’accès aux données utilise l’API ODBC pour dialoguer avec un SGBD relationnel. La configuration de la connexion est centralisée dans une classe GestionnaireConnexion.
4.1 Configuraton et pool de connexions
Pour éviter la création répétée de connexions, un pool de QSqlDatabase est maintenu. Chaque thread de travail emprunte une connexion au pool et la restitue après utilisation.
class PoolConnexions {
QQueue<QSqlDatabase> disponibles;
QMutex mutex;
public:
QSqlDatabase obtenirConnexion() {
QMutexLocker locker(&mutex);
if (!disponibles.isEmpty()) return disponibles.dequeue();
QSqlDatabase db = QSqlDatabase::addDatabase("QODBC",
QUuid::createUuid().toString());
db.setDatabaseName("hotel_dsn");
db.open();
return db;
}
void restituer(QSqlDatabase db) {
QMutexLocker locker(&mutex);
disponibles.enqueue(db);
}
};
4.2 Construction et optimisation des requêtes
Les requêtes sont construites avec des paramètres liés pour éviter les injections SQL. Les index sont créés sur les colonnes souvent utilisées dans les clauses WHERE (ex. id_chambre, date_arrivee).
QSqlQuery requete;
requete.prepare("SELECT nom, prenom FROM clients WHERE id_client = ?");
requete.addBindValue(id);
requete.exec();
L’optimisation passe aussi par la limitation des colonnes retournées et l’utilisation de jointures ciblées.
- Interface utilisateur graphique
Le choix de la bibliothèque graphique influence la portabilité et la richesse de l’interface. Deux alternatives sont envisagées : MFC (spécifique à Windows) et Qt (multiplateforme).
5.1 Organisation des fenêtres
La fenêtre principale s’articule autour d’un QStackedWidget (ou équivalent MFC) permettant de basculer entre plusieurs vues : tableau des chambres, formulaire de réservation, historique des clients.
5.2 Vue du tableau des chambres
Cette vue affiche une grille où chaque cellule représente une chambre avec un code couleur correspondant à son état. Un clic droit ouvre un menu contextuel pour réserver, lancer le nettoyage ou déclencher une maintenance.
void initialiserGrilleChambres(QTableWidget* grille, const QList<Chambre>& chambres) {
grille->setRowCount(chambres.size());
for (int i = 0; i < chambres.size(); ++i) {
auto itemNumero = new QTableWidgetItem(QString::number(chambres[i].numero));
itemNumero->setBackground(couleurEtat(chambres[i].etat));
grille->setItem(i, 0, itemNumero);
}
}
5.3 Formulaire de réservation
Le formulaire collecte les informations client et les dates de séjour. Une validation est effectuée avant soumission (disponibilité, cohérence des dates).
if (dateArrivee >= dateDepart) {
QMessageBox::warning(this, "Erreur", "La date de départ doit être postérieure à l'arrivée.");
return;
}
- Fonctionnalités avancées et personnalisation
Plusieurs extensions améliorent l’expérience utilisateur : contrôles personnalisés, gestion des ressources, et impression de factures.
6.1 Contrôles sur mesure
Un sélecteur de période, héritant de QWidget, combine deux champs de date avec des flèches de navigation. Il émet un signal plageModifiee(QDate debut, QDate fin).
6.2 Gestion des ressources de l’application
Les icônes, images et chaînes traduites sont stockées dans un fichier .qrc (Qt Resource Collection) et accessibles via le préfixe :/.
QPixmap iconeCle = QPixmap(":/icones/cle.png");
6.3 Traitement des départs et édition des factures
Au moment du départ, le système calcule le montant total (nuitées, services, taxes) et génère un document PDF. L’impression est ensuite déclenchée via une imprimante configurée.
void Facture::genererPDF(const Client& client, const Sejour& sejour) {
QPdfWriter pdf("facture_" + QString::number(sejour.id) + ".pdf");
QPainter painter(&pdf);
// ... dessin des lignes de facture
painter.end();
}
La gestion complète des clients (création, consultation, modification, suppression) est accessible depuis le module administrateur et respecte les accès basés sur les rôles.