Loading...

12-Hour Money-Back Guarantee

Design (LLD) Hotel Management System - C++

Design (LLD) Hotel Management System - C++

Design (LLD) Hotel Management System - C++

26 Feb 20267 min read

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:

  1. Search room → Available

  2. Before booking finalizes, another booking call happens

  3. Both see room as available

  4. Double booking happens

This is not thread-race. This is check-then-act problem.

❌ Why It Happens

Because:

searchAvailable()
confirm()

are separate operations.

✅ Solution