Bird
Raised Fist0

Hotel Management System

LLDmediumInterview freq: medium
Choose your preparation mode4 modes available
๐ŸŽฏ
Hotel Management System - Rooms, Housekeeping, Billing, Concurrency
40 min ยท medium
medium

Design a hotel management system for a hotel with 5 floors and 4 gates. The system must manage room bookings, housekeeping schedules, and billing with concurrency control to prevent double-booking and inconsistent billing.

  • Hotel has 5 floors, each with multiple rooms of different types (Single, Double, Suite).
  • Rooms can be booked concurrently by multiple users; system must prevent double-booking.
  • Housekeeping staff update room status (Clean, Dirty, In-Progress) asynchronously.
  • Billing must handle concurrent updates from room service and front desk.
  • Pricing structure varies by room type and season.
  • System must allow extensibility for adding new room types and billing rules without modifying existing classes.
๐Ÿ“‹
Guided Steps
1/5
Step 1
Define Room and RoomType with proper state encapsulation
Design classes to represent Room and RoomType. How will you represent room types and their pricing? How will you store room state such as availability?
๐Ÿ’ก Hint
Use enum for RoomType to ensure type safety and exhaustive switch. Use private fields with synchronized getters/setters for room availability state.
Use enum RoomType for fixed set of room categories to prevent runtime typo bugs; Room class has private volatile boolean isAvailable to reflect availability state with visibility guarantees.
Mid: Uses enum for RoomType and encapsulates availability state; Senior: Explains volatile ensures visibility of isAvailable changes across threads preventing stale reads.
โš ๏ธCommon Wrong Answer
Using String for room type leads to typo bugs and no compile-time checks; using non-volatile boolean causes threads to see stale availability state causing double-booking.
Step 2
Implement thread-safe room booking method
How will you implement booking a room to prevent race conditions when multiple threads try to book the same room?
๐Ÿ’ก Hint
Use synchronized method or block to atomically check and update room availability.
public synchronized boolean bookRoom() to atomically check isAvailable and set it to false, preventing race conditions.
Mid: Uses synchronized to protect booking; Senior: Traces thread interleaving where Thread 1 reads isAvailable=true, context switch, Thread 2 reads isAvailable=true, both book leading to double-booking without synchronization.
โš ๏ธCommon Wrong Answer
Using unsynchronized booking method causes race conditions and double-booking due to lost updates.
Step 3
Design Housekeeping status updates with concurrency control
Housekeeping staff update room status asynchronously. How will you design the RoomStatus and ensure thread-safe updates?
๐Ÿ’ก Hint
Use enum for RoomStatus and synchronized setter methods or AtomicReference for status updates.
Use enum RoomStatus (CLEAN, DIRTY, IN_PROGRESS) and AtomicReference<RoomStatus> status to allow lock-free thread-safe updates.
Mid: Uses enum for status and thread-safe updates; Senior: Explains AtomicReference allows atomic compare-and-set operations preventing lost updates without blocking.
โš ๏ธCommon Wrong Answer
Using String for status and non-atomic updates leads to inconsistent room status visible to different threads.
Step 4
Design Billing system with extensible pricing and concurrency safety
How will you design the billing component to handle concurrent updates from multiple sources and allow adding new pricing rules without modifying existing code?
๐Ÿ’ก Hint
Use abstract class or interface for BillingRule with concrete implementations; use synchronized methods or AtomicInteger/AtomicLong for concurrent amount updates.
abstract class BillingRule with abstract method calculateCharge(Room room); Billing class uses AtomicLong totalAmount with synchronized addCharge(long) to ensure thread-safe updates.
Mid: Uses abstract class for shared state and behavior; Senior: Explains abstract class holds shared fields unlike interface, synchronized addCharge prevents race conditions updating totalAmount.
โš ๏ธCommon Wrong Answer
Using interface alone for BillingRule without shared state forces code duplication; using non-atomic long for amount causes lost updates under concurrency.
Step 5
Ensure extensibility and maintainability using SOLID principles
How will your design allow adding new room types, housekeeping statuses, or billing rules without modifying existing classes?
๐Ÿ’ก Hint
Use Open-Closed Principle by leveraging enums for fixed sets and abstract classes/interfaces for extensible behaviors.
Use enum for fixed sets (RoomType, RoomStatus) and abstract class BillingRule for extensible billing logic, adhering to Open-Closed Principle; avoid modifying existing classes when adding new types.
Mid: Recognizes Open-Closed Principle; Senior: Explains why abstract class allows adding new billing rules without changing existing code, enums prevent invalid values at compile time.
โš ๏ธCommon Wrong Answer
Hardcoding room types or billing logic in concrete classes violates Open-Closed Principle, requiring code changes and risking regressions.
๐Ÿ”Œ
API Design Challenge
Design RESTful APIs for room booking, housekeeping status updates, and billing operations that handle concurrency and ensure data consistency.
  • Use optimistic locking or versioning to prevent lost updates in concurrent bookings.
  • Design idempotent endpoints for housekeeping status updates to handle retries safely.
  • Use atomic operations or transactions in billing APIs to ensure consistent total amounts.
๐Ÿ“Š
Scoring Rubric
โ–ผ Show
Must Have - 60%
  • Use enum for RoomType and RoomStatus to prevent runtime errors
  • Room availability state declared volatile for visibility
  • Synchronized booking method to prevent race conditions
  • AtomicReference or synchronized methods for housekeeping status updates
  • Abstract class for BillingRule to hold shared state and behavior
Good to Have - 30%
  • AtomicLong for billing amount updates
  • Clear composition and aggregation relationships with lifecycle ownership
  • Adherence to Open-Closed Principle for extensibility
  • Detailed concurrency reasoning with thread interleaving examples
Bonus - 10%
  • Use of double-checked locking with volatile for singleton Hotel instance
  • Use of Java 8+ concurrency utilities for improved performance
Using String for RoomType or RoomStatus leads to typo bugs and no compile-time checks, causing runtime errors and inconsistent state.
Non-volatile boolean for availability causes stale reads and double-booking due to lack of visibility guarantees across threads.
โœ…
Model Answer
โ–ผ Reveal

This design uses Java enums for RoomType and RoomStatus to ensure type safety and prevent runtime errors. Room availability is managed with a private volatile boolean to guarantee visibility across threads. The booking method is synchronized to prevent race conditions and double-booking. Housekeeping status updates use AtomicReference for lock-free thread-safe updates. Billing uses an abstract class BillingRule for extensible pricing logic and AtomicLong with synchronized methods to handle concurrent amount updates. The design adheres to the Open-Closed Principle, allowing new room types, statuses, and billing rules without modifying existing classes.

  • Use enum RoomType and RoomStatus for fixed sets to prevent invalid values and runtime errors.
  • Declare isAvailable as volatile boolean in Room to ensure visibility of state changes across threads.
  • Synchronize bookRoom() method to atomically check and update availability, preventing race conditions.
  • Use AtomicReference<RoomStatus> for thread-safe, lock-free housekeeping status updates.
  • Implement BillingRule as abstract class to hold shared state and behavior, enabling extensibility.
  • Use AtomicLong and synchronized addCharge in Billing to prevent lost updates under concurrency.
  • Adhere to Open-Closed Principle by designing extensible abstractions and avoiding modifications to existing classes when adding new features.
  • Use enum RoomType and RoomStatus to enforce type safety and prevent runtime errors.
  • Room class uses private volatile boolean isAvailable to ensure visibility of availability state changes across threads.
  • Synchronized bookRoom() method to atomically check and update room availability preventing double-booking.
  • Use AtomicReference<RoomStatus> for thread-safe housekeeping status updates without blocking.
  • Abstract class BillingRule with shared state and behavior for extensible billing logic, interface cannot hold instance state in Java.
  • Billing class uses AtomicLong totalAmount with synchronized addCharge(long) to prevent lost updates under concurrency.
  • Adhere to Open-Closed Principle by designing extensible abstractions and using enums for fixed sets.
  • Composition relationship: Hotel contains Floors; Floor contains Rooms; Billing aggregates BillingRules; Housekeeping associates with Rooms.
  • Lifecycle: Rooms exist independently of Housekeeping and Billing; BillingRules can be added without modifying Billing class.
  • Concurrency control uses volatile, synchronized, and atomic classes to ensure thread safety and visibility guarantees.

Practice

(1/5)
1. In a Card Game Framework, the Deck class has a private constructor and a static synchronized getInstance() method returning the single deck instance. Which design pattern is applied here and why is this the correct choice?
easy
A. Factory Method pattern to create different types of decks dynamically.
B. Singleton pattern to ensure only one deck instance exists across the game.
C. Builder pattern to construct complex deck configurations step-by-step.
D. Prototype pattern to clone decks for multiple players efficiently.

Solution

  1. Step 1: Identify the pattern from constructor and method

    The private constructor prevents external instantiation, and the static synchronized getInstance() method ensures a single shared instance, matching Singleton pattern characteristics.
  2. Step 2: Why Singleton fits this scenario

    In a card game, having a single deck instance avoids inconsistencies and duplication, ensuring all players draw from the same deck state.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Private constructor + static synchronized getInstance() -> Singleton [check]
Hint: Private constructor + static getInstance() -> Singleton [check]
Common Mistakes:
2. In designing the Player abstraction for the Tic-Tac-Toe game, should Player be an abstract class or an interface, and why?
easy
A. Abstract class, because Java interfaces cannot have default methods for shared logic.
B. Interface, because players only define behavior like making moves, allowing multiple inheritance of behavior.
C. Abstract class, because players share common state like name and score that should be inherited.
D. Interface, because it allows storing player state directly in the interface.

Solution

  1. Step 1: Analyze state and behavior requirements

    Players have common state (e.g., name, symbol, score) and some shared behavior (e.g., basic validation). Abstract classes allow state fields and shared method implementations.
  2. Step 2: Why abstract class is better than interface here

    Interfaces define behavior contracts but cannot hold instance state. Although Java 8+ interfaces support default methods, they cannot hold mutable state. Thus, abstract class suits better for shared state and behavior.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Shared state + behavior -> abstract class, not interface [check]
Hint: State needs abstract class; behavior-only fits interface [check]
Common Mistakes:
3. A Java class has a private constructor and a static synchronized getInstance() method returning the single instance. Which design pattern is applied and why is this the CORRECT use case?
easy
A. Factory pattern, because it controls object creation centrally.
B. Builder pattern, because it constructs complex objects step-by-step.
C. Prototype pattern, because it clones existing instances for reuse.
D. Singleton pattern, because it ensures only one instance with controlled access.

Solution

  1. Step 1: Identify the pattern by constructor and method

    The private constructor prevents external instantiation, and the static synchronized getInstance() method controls access to a single instance, matching Singleton pattern characteristics.
  2. Step 2: Eliminate other patterns

    Factory creates multiple instances, Builder constructs complex objects with multiple steps, and Prototype clones objects; none restrict to a single instance.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Private constructor + synchronized static getter -> Singleton pattern [check]
Hint: Private constructor + static synchronized getter -> Singleton [check]
Common Mistakes:
4. In the ESPNcricinfo system, the Scorecard class has a recordBall() method that checks if the ball is valid and then updates the score. Without synchronizing recordBall(), what exact sequence of Thread 1 and Thread 2 causes double-counting of runs?
medium
A. Thread 1 and Thread 2 both call recordBall() simultaneously and both update the score atomically.
B. Thread 1 checks ball validity -> valid, context switch; Thread 2 checks ball validity -> valid; both update score causing double-count.
C. Thread 1 updates score, then Thread 2 reads stale score and overwrites it with old value.
D. Thread 1 and Thread 2 both wait on a lock causing deadlock and no score update.

Solution

  1. Step 1: Identify check-then-act race condition

    Both threads check ball validity before updating score; without synchronization, both see valid state.
  2. Step 2: Trace interleaving causing double-count

    Thread 1 checks valid -> context switch; Thread 2 checks valid -> both proceed to update score independently causing double-count.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Check-then-act gap without sync -> double update [check]
Hint: Check-then-act gap -> both threads proceed -> double update [check]
Common Mistakes:
5. You need to add ElectricCar support with ChargingSpots to a parking lot system using the State pattern. What is the minimum change to support this without modifying existing classes?
hard
A. Modify Vehicle base class to add isElectric flag and add charging logic in existing Spot classes
B. Create a new ElectricVehicle interface and modify all existing Vehicle subclasses to implement it
C. Add ELECTRIC_CAR to VehicleType enum and CHARGING to SpotType enum, create ElectricCar subclass, update SpotCompatibility map
D. Add charging logic inside the existing State classes handling parking spots

Solution

  1. Step 1: Identify extensibility requirements

    Adding ElectricCar and ChargingSpot requires new types without modifying existing classes to respect OCP.
  2. Step 2: Evaluate options

    Add ELECTRIC_CAR to VehicleType enum and CHARGING to SpotType enum, create ElectricCar subclass, update SpotCompatibility map adds new enum values and subclasses without changing existing classes, enabling extension. Options B, C, and D modify existing classes or interfaces, violating OCP.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    New enums and subclass -> no existing class modified [check]
Hint: Add new enums and subclasses, avoid modifying existing classes [check]
Common Mistakes:
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard