Bird
Raised Fist0

Design a Movie Ticket Booking System - Seats, Concurrency Lock, Expiry, Payment

LLDmediumInterview freq: high
Choose your preparation mode4 modes available
▶
Class Trace
Step 1/4
SeatLockExpiryManager Singleton

SeatLockExpiryManager is a Singleton with a volatile instance field to ensure safe lazy initialization.

🎯 Tests whether candidate knows volatile prevents JVM reordering allowing safe double-checked locking in Singleton pattern.
public class SeatLockExpiryManager {
  private static volatile SeatLockExpiryManager instance;
  private SeatLockExpiryManager() { }
  public static SeatLockExpiryManager getInstance() {
    if (instance == null) {
      synchronized(SeatLockExpiryManager.class) {
        if (instance == null) {
          instance = new SeatLockExpiryManager();
        }
      }
    }
    return instance;
  }
}
Show composition with Seat and Observer pattern

Show composes multiple Seat objects and maintains observers to notify on seat state changes.

🎯 Tests understanding of composition lifecycle ownership and Observer pattern for seat availability updates.
public class Show {
  private List<Seat> seats = new ArrayList<>();
  private List<Observer> observers = new ArrayList<>();
  public void addObserver(Observer o) {
    observers.add(o);
  }
  private void notifyObservers() {
    for (Observer o : observers) {
      o.update();
    }
  }
}
Seat synchronized lock/unlock methods

Seat methods lock() and unlock() are synchronized to ensure atomic state changes preventing race conditions.

🎯 Tests knowledge that synchronized methods provide intrinsic lock preventing concurrent inconsistent seat locking.
public class Seat {
  private boolean isLocked = false;
  public synchronized boolean lock() {
    if (isLocked) return false;
    isLocked = true;
    return true;
  }
  public synchronized void unlock() {
    isLocked = false;
  }
}
PaymentProcessor association

PaymentProcessor is associated with Booking but has independent lifecycle, allowing different payment implementations.

🎯 Tests understanding of association relationship and OCP by allowing PaymentProcessor interface to be extended.
public interface PaymentProcessor {
  boolean processPayment(PaymentDetails details);
}
public class Booking {
  private PaymentProcessor paymentProcessor;
  public Booking(PaymentProcessor processor) {
    this.paymentProcessor = processor;
  }
  public boolean pay(PaymentDetails details) {
    return paymentProcessor.processPayment(details);
  }
}
📐
Class Diagram
Class Diagram
       +----------------------+
       |      <<Singleton>>    |
       | SeatLockExpiryManager |
       +----------------------+
       | - volatile instance   |
       | - expiryQueue: Queue  |
       +----------------------+
       | + getInstance(): this |
       | + startExpiryThread() |
       +----------------------+
              │
              │ composition
              ▼
       +----------------------+
       |        Show          |
       +----------------------+
       | - seats: List<Seat>  |
       | - observers: List<Observer> |
       +----------------------+
       | + addObserver(Obs)   |
       | + notifyObservers()  |
       +----------------------+
              │ composition
              ▼
       +----------------------+
       |        Seat          |
       +----------------------+
       | - seatNumber: String |
       | - isLocked: boolean  |
       +----------------------+
       | + synchronized lock()|
       | + synchronized unlock()|
       +----------------------+
              │ association
              ▼
       +----------------------+
       |  PaymentProcessor    |
       +----------------------+
       | + processPayment()   |
       +----------------------+

This class diagram illustrates key Java-specific design decisions in a Movie Ticket Booking System. The SeatLockExpiryManager is implemented as a Singleton with a volatile instance field to ensure thread-safe lazy initialization, preventing partially constructed instances from being visible. The Show class composes Seat objects, owning their lifecycle, and implements the Observer pattern to notify listeners of seat state changes, enforcing loose coupling and extensibility. Seat locking methods are synchronized to guarantee atomic state transitions, preventing race conditions during concurrent bookings. PaymentProcessor is modeled as an interface associated with Booking, allowing multiple payment implementations without affecting Booking's lifecycle, adhering to the Open-Closed Principle. These decisions collectively ensure thread safety, extensibility, and clear lifecycle ownership in Java.

Primary Focus

✓ Singleton volatile + Observer pattern usage + Composition vs Association lifecycle

Practice

(1/5)
1. In designing a notification channel abstraction, should NotificationChannel be an abstract class or an interface? Consider that each channel has state like connection status and common methods like connect() and send(). Why?
easy
A. Abstract class, because it can hold shared state and provide default method implementations.
B. Abstract class, because interfaces cannot define method signatures.
C. Interface, because Java interfaces can now have default methods with state variables.
D. Interface, because it allows multiple inheritance and all methods are stateless contracts.

Solution

  1. Step 1: Analyze state and behavior requirements

    NotificationChannel needs to hold connection status (state) and provide common method implementations.
  2. Step 2: Choose between interface and abstract class

    Interfaces cannot hold instance state; abstract classes can hold state and provide default behavior, making abstract class the correct choice.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Abstract class supports shared state and default methods [check]
Hint: Stateful base -> abstract class; stateless contract -> interface [check]
Common Mistakes:
2. In the brokerage system, should the Order entity be designed as an abstract class or an interface? Consider that orders have common state fields like orderId and timestamp, but different types of orders have distinct execution logic.
easy
A. Abstract class, because it can hold shared state and provide default implementations for common behavior.
B. Interface, because Java 8+ interfaces can have default methods and fields for state.
C. Interface, because it allows multiple inheritance and enforces method signatures without state.
D. Abstract class, because it forces all subclasses to implement all methods explicitly.

Solution

  1. Step 1: Analyze state vs behavior requirements

    Orders share common state fields like orderId and timestamp, which require storage and possibly shared methods.
  2. Step 2: Why abstract class fits better

    Abstract classes can hold state and provide default method implementations, reducing code duplication. Interfaces cannot hold instance state.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Shared state + default behavior -> abstract class [check]
Hint: Stateful base class -> abstract class, not interface [check]
Common Mistakes:
3. The ATM system currently calculates transaction fees using a FeeCalculator class. To add a WeekendFeeStrategy without modifying existing classes, which SOLID principle applies and what is the minimal correct change?
medium
A. Add a boolean flag isWeekend in FeeCalculator and branch logic accordingly, maintaining SRP.
B. Modify FeeCalculator to add weekend logic, violating OCP but keeping code simple.
C. Create WeekendFeeStrategy implementing FeeStrategy interface; FeeCalculator uses FeeStrategy via dependency injection, adhering to OCP and DIP.
D. Create WeekendFeeStrategy subclassing FeeCalculator and override fee calculation, violating DIP.

Solution

  1. Step 1: Identify applicable SOLID principles

    Open-Closed Principle (OCP) requires FeeCalculator not be modified for new fee types. Dependency Inversion Principle (DIP) requires FeeCalculator depend on abstractions.
  2. Step 2: Apply correct design

    Create WeekendFeeStrategy implementing FeeStrategy interface. Inject FeeStrategy into FeeCalculator. This adds new behavior without modifying existing code.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    New strategy class + DI -> OCP and DIP respected [check]
Hint: New fee? Add strategy class, inject via interface [check]
Common Mistakes:
4. The system needs to support ElectricCar playback devices that require ChargingSpot management. What is the minimum change to the current design to support this without modifying existing classes?
hard
A. Add ELECTRIC_CAR to DeviceType enum, CHARGING to SpotType enum, create ElectricCar extends Device, and update SpotCompatibility map externally.
B. Modify existing Device class to add charging logic and add ElectricCar as a flag inside it.
C. Create a new ElectricCar class unrelated to Device and duplicate playback logic.
D. Add charging logic inside the PlaybackQueue class to handle ElectricCar specifics.

Solution

  1. Step 1: Identify extensibility without modifying existing classes

    Adding new enum values and subclassing allows extension without changing existing class code.
  2. Step 2: Update compatibility externally

    Updating SpotCompatibility map outside existing classes respects OCP and avoids class explosion or god-class logic.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Extend enums + subclass + external map update -> minimal change [check]
Hint: Extend enums + subclass + external map -> minimal change [check]
Common Mistakes:
5. The system needs to support ElectricCar and ChargingSpot without modifying existing Vehicle or Spot classes. What is the minimum change to support this extensibility in Java?
hard
A. Modify Vehicle class to include a boolean isElectric and add charging logic inside existing methods.
B. Add ELECTRIC_CAR to VehicleType enum, CHARGING to SpotType enum, create ElectricCar subclass, and update compatibility map externally.
C. Create a new ElectricVehicle interface and modify Spot to check for it explicitly.
D. Add charging logic inside Vehicle and Spot classes directly, breaking encapsulation.

Solution

  1. Step 1: Identify extensibility without modifying existing classes

    Enums can be extended with new constants; subclassing ElectricCar adds new behavior without changing Vehicle.
  2. Step 2: Update compatibility externally

    Updating SpotCompatibility.COMPATIBILITY_MAP externally respects OCP and avoids class explosion or god classes.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Extend enums + subclass + external map update -> minimal change [check]
Hint: Extend enums + subclass + external map -> minimal change [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