Design (LLD) Hotel Management System - C++
1️⃣ Functional Requirements
A hotel system should support:
Add hotel rooms
Search rooms (by type, price, availability)
Book room
Cancel booking
Check-in
Check-out
Generate bill
Payment processing
Room pricing strategy
Different room types (Single, Double, Suite)
2️⃣ Core Entities
Room
Guest
Booking
Payment
Invoice
Hotel
Pricing Strategy
Notification Service
3️⃣ Design Patterns Used (With Reason)
| Pattern | Where Used | Why |
|---|---|---|
| Singleton | HotelSystem | Only one central system instance |
| Factory Method | RoomFactory | Create different room types |
| Strategy | PricingStrategy | Flexible pricing logic |
| State | BookingState | Booking lifecycle management |
| Observer | NotificationService | Notify guest on booking events |
| Decorator | RoomServiceDecorator | Add extra services to booking |
| Builder | InvoiceBuilder | Step-by-step invoice creation |
| Repository | BookingRepository | Abstract data storage |
4️⃣ Algorithms Used
1. Room Allocation Algorithm
Linear scan for available rooms
Can be upgraded to priority queue
2. Date Overlap Detection
Overlap if:
!(end1 <= start2 || end2 <= start1)
3. Billing Calculation
Total = BasePrice + Services - Discounts
4. Search Algorithm
Filter by type
Filter by price range
Check availability
5️⃣ COMPLETE C++
#include <iostream>
#include <vector>
#include <string>
#include <memory>
#include <map>
#include <algorithm>
using namespace std;
///////////////////////////////////////////////////////////
// ENUMS
///////////////////////////////////////////////////////////
enum class RoomType { SINGLE, DOUBLE, SUITE };
enum class BookingStatus { CREATED, CONFIRMED, CHECKED_IN, COMPLETED, CANCELLED };
///////////////////////////////////////////////////////////
// STRATEGY PATTERN - Pricing Strategy
///////////////////////////////////////////////////////////
class PricingStrategy {
public:
virtual double calculatePrice(double basePrice, int nights) = 0;
virtual ~PricingStrategy() {}
};
class RegularPricing : public PricingStrategy {
public:
double calculatePrice(double basePrice, int nights) override {
return basePrice * nights;
}
};
class WeekendPricing : public PricingStrategy {
public:
double calculatePrice(double basePrice, int nights) override {
return basePrice * nights * 1.2; // 20% increase
}
};
///////////////////////////////////////////////////////////
// OBSERVER PATTERN
///////////////////////////////////////////////////////////
class Observer {
public:
virtual void update(string message) = 0;
};
class Guest : public Observer {
private:
string name;
public:
Guest(string name) : name(name) {}
void update(string message) override {
cout << "Notification for " << name << ": " << message << endl;
}
string getName() { return name; }
};
///////////////////////////////////////////////////////////
// ROOM CLASS
///////////////////////////////////////////////////////////
class Room {
protected:
int roomNumber;
RoomType type;
double basePrice;
bool available;
public:
Room(int number, RoomType t, double price)
: roomNumber(number), type(t), basePrice(price), available(true) {}
virtual ~Room() {}
int getRoomNumber() { return roomNumber; }
RoomType getType() { return type; }
double getBasePrice() { return basePrice; }
bool isAvailable() { return available; }
void setAvailability(bool status) { available = status; }
};
///////////////////////////////////////////////////////////
// FACTORY METHOD PATTERN
///////////////////////////////////////////////////////////
class RoomFactory {
public:
static shared_ptr<Room> createRoom(RoomType type, int number) {
switch(type) {
case RoomType::SINGLE:
return make_shared<Room>(number, type, 100);
case RoomType::DOUBLE:
return make_shared<Room>(number, type, 200);
case RoomType::SUITE:
return make_shared<Room>(number, type, 500);
}
return nullptr;
}
};
///////////////////////////////////////////////////////////
// STATE PATTERN
///////////////////////////////////////////////////////////
class Booking;
class BookingState {
public:
virtual void next(Booking* booking) = 0;
virtual string getStatus() = 0;
};
///////////////////////////////////////////////////////////
// DECORATOR PATTERN - Extra Services
///////////////////////////////////////////////////////////
class RoomService {
public:
virtual double cost() = 0;
virtual string description() = 0;
virtual ~RoomService() {}
};
class BasicRoomService : public RoomService {
public:
double cost() override { return 0; }
string description() override { return "Basic Room"; }
};
class SpaDecorator : public RoomService {
private:
shared_ptr<RoomService> service;
public:
SpaDecorator(shared_ptr<RoomService> s) : service(s) {}
double cost() override {
return service->cost() + 50;
}
string description() override {
return service->description() + " + Spa";
}
};
///////////////////////////////////////////////////////////
// BOOKING CLASS
///////////////////////////////////////////////////////////
class Booking {
private:
int bookingId;
shared_ptr<Room> room;
shared_ptr<Guest> guest;
int nights;
shared_ptr<PricingStrategy> pricingStrategy;
shared_ptr<RoomService> roomService;
public:
Booking(int id, shared_ptr<Room> r, shared_ptr<Guest> g, int nights,
shared_ptr<PricingStrategy> strategy)
: bookingId(id), room(r), guest(g), nights(nights),
pricingStrategy(strategy) {
roomService = make_shared<BasicRoomService>();
}
void addService(shared_ptr<RoomService> service) {
roomService = service;
}
double calculateBill() {
double base = pricingStrategy->calculatePrice(room->getBasePrice(), nights);
return base + roomService->cost();
}
void confirm() {
room->setAvailability(false);
guest->update("Booking Confirmed!");
}
void cancel() {
room->setAvailability(true);
guest->update("Booking Cancelled!");
}
int getId() { return bookingId; }
};
///////////////////////////////////////////////////////////
// BUILDER PATTERN - Invoice
///////////////////////////////////////////////////////////
class Invoice {
public:
double amount;
string details;
};
class InvoiceBuilder {
private:
Invoice invoice;
public:
InvoiceBuilder& setAmount(double amt) {
invoice.amount = amt;
return *this;
}
InvoiceBuilder& setDetails(string det) {
invoice.details = det;
return *this;
}
Invoice build() {
return invoice;
}
};
///////////////////////////////////////////////////////////
// REPOSITORY PATTERN
///////////////////////////////////////////////////////////
class BookingRepository {
private:
map<int, shared_ptr<Booking>> bookings;
public:
void save(shared_ptr<Booking> booking) {
bookings[booking->getId()] = booking;
}
shared_ptr<Booking> find(int id) {
return bookings[id];
}
};
///////////////////////////////////////////////////////////
// SINGLETON - HOTEL SYSTEM
///////////////////////////////////////////////////////////
class HotelSystem {
private:
vector<shared_ptr<Room>> rooms;
BookingRepository bookingRepo;
HotelSystem() {}
public:
static HotelSystem& getInstance() {
static HotelSystem instance;
return instance;
}
void addRoom(RoomType type, int number) {
rooms.push_back(RoomFactory::createRoom(type, number));
}
shared_ptr<Room> searchAvailable(RoomType type) {
for (auto& room : rooms) {
if (room->getType() == type && room->isAvailable())
return room;
}
return nullptr;
}
shared_ptr<Booking> bookRoom(int bookingId, shared_ptr<Guest> guest,
RoomType type, int nights) {
auto room = searchAvailable(type);
if (!room) {
cout << "No room available\n";
return nullptr;
}
auto strategy = make_shared<RegularPricing>();
auto booking = make_shared<Booking>(bookingId, room, guest, nights, strategy);
booking->confirm();
bookingRepo.save(booking);
return booking;
}
void checkout(int bookingId) {
auto booking = bookingRepo.find(bookingId);
if (!booking) return;
double bill = booking->calculateBill();
Invoice invoice = InvoiceBuilder()
.setAmount(bill)
.setDetails("Hotel Stay Charges")
.build();
cout << "Invoice Generated: " << invoice.amount << endl;
}
};
///////////////////////////////////////////////////////////
// MAIN
///////////////////////////////////////////////////////////
int main() {
HotelSystem& system = HotelSystem::getInstance();
system.addRoom(RoomType::SINGLE, 101);
system.addRoom(RoomType::DOUBLE, 102);
auto guest = make_shared<Guest>("John");
auto booking = system.bookRoom(1, guest, RoomType::SINGLE, 3);
if (booking) {
auto service = make_shared<SpaDecorator>(
make_shared<BasicRoomService>());
booking->addService(service);
system.checkout(1);
}
return 0;
}
6️⃣ Complexity Analysis
| Operation | Complexity |
|---|---|
| Add Room | O(1) |
| Search Room | O(n) |
| Booking | O(n) |
| Billing | O(1) |
| Repository Lookup | O(log n) |
7️⃣ How to Improve for Production?
Use priority queue for optimized allocation
Use interval tree for date-based booking
Add payment gateway integration
Make it thread-safe
Replace in-memory repo with DB
8️⃣ Interview Discussion Points
If asked:
Why Strategy? → Pricing flexibility
Why State? → Booking lifecycle control
Why Decorator? → Add services dynamically
Why Repository? → Abstraction from storage
Why Singleton? → Centralized management
Why Factory? → Room object creation abstraction
Problems in this implementation:
We built a single-threaded Hotel Management System. Now let’s analyze:
❗ What problems still exist in a this single-threaded design? ❗ Why is single-threaded not enough in real systems? ❗ How do we fix each issue?
🚨 1️⃣ Double Booking Problem (Logical Race Condition)
🔴 Problem
Even in single-threaded systems, you can still have logical race conditions.
Example:
Search room → Available
Before booking finalizes, another booking call happens
Both see room as available
Double booking happens
This is not thread-race. This is check-then-act problem.
❌ Why It Happens
Because:
searchAvailable()
confirm()
are separate operations.
