Bird
Raised Fist0

Design an ATM System - States, Transitions, Card, PIN, Transaction, Security

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

ATM is a Singleton class with a volatile instance to ensure safe lazy initialization in multithreaded context.

🎯 Tests whether candidate knows volatile on Singleton instance prevents JVM reordering that could expose partially constructed object.
public class ATM {
  private static volatile ATM instance;
  private ATM() { }
  public static ATM getInstance() {
    if (instance == null) {
      synchronized (ATM.class) {
        if (instance == null) {
          instance = new ATM();
        }
      }
    }
    return instance;
  }
}
Composition between ATM and SecurityModule

ATM composes SecurityModule, meaning ATM owns SecurityModule lifecycle and instantiates it internally.

🎯 Tests whether candidate distinguishes composition (strong ownership) from association and explains lifecycle dependency in Java.
public class ATM {
  private final SecurityModule security = new SecurityModule();
  public void start() {
    security.verifyPin("1234");
  }
}
Abstract Transaction class

Transaction is an abstract class holding shared state (amount) and synchronized execute() method to ensure thread-safe transaction processing.

🎯 Tests whether candidate understands abstract class is needed over interface because Transaction holds state and synchronized method enforces atomicity.
public abstract class Transaction {
  protected double amount;
  public synchronized void execute() {
    // transaction logic
  }
}
Association between SecurityModule and BankServer

SecurityModule holds a reference to BankServer but does not own its lifecycle, indicating association with independent lifecycle.

🎯 Tests whether candidate correctly identifies association and explains that BankServer can exist independently of SecurityModule.
public class SecurityModule {
  private BankServer bankServer;
  public SecurityModule(BankServer bs) {
    this.bankServer = bs;
  }
  public boolean verifyPin(String pin) {
    return bankServer.validateCard(pin);
  }
}
📐
Class Diagram
Class Diagram
       +----------------+
       |     ATM        |
       | <<Singleton>>  |
       | -state: State  |
       | -security: Sec |
       | -bankServer:BS |
       |----------------|
       | +start() [sync]|
       | +insertCard()  |
       | +ejectCard()   |
       +----------------+
              │◆
              │ composition (ATM owns SecurityModule lifecycle)
              │
       +--------------------+
       | SecurityModule     |
       |--------------------|
       | -pinVerified:bool  |
       |--------------------|
       | +verifyPin(pin):bool|
       | +lock()            |
       +--------------------+
              │
              │ association (SecurityModule uses BankServer independently)
              │
       +----------------+
       | BankServer     |
       |----------------|
       | +validateCard()|
       | +processTxn()  |
       +----------------+
              │
              │ association (BankServer independent lifecycle)
              │
       +----------------+
       | Transaction    |
       | <<Abstract>>   |
       |----------------|
       | -amount: double|
       |----------------|
       | +execute() [sync]|
       +----------------+

This ATM system design uses a Singleton pattern for the ATM class with a volatile instance to ensure thread-safe lazy initialization. The ATM composes SecurityModule, enforcing strong lifecycle ownership and encapsulating security concerns. SecurityModule associates with BankServer, which has an independent lifecycle, reflecting real-world separation of concerns. The Transaction class is abstract, holding shared state and synchronized behavior to guarantee thread-safe execution of transactions. These design decisions demonstrate careful use of Java-specific constructs like volatile, synchronized methods, composition vs association, and abstract classes to enforce SOLID principles and concurrency safety.

Primary Focus

✓ Composition vs Association lifecycle + State Machine abstraction + Singleton volatile usage

Practice

(1/5)
1. In the Ride Booking system, the FareCalculator class has a private constructor and a static synchronized getInstance() method. Which design pattern is applied here and why is this the correct use case?
easy
A. Factory pattern, because it creates FareCalculator instances on demand.
B. Prototype pattern, to clone FareCalculator instances for thread safety.
C. Singleton pattern, to ensure only one FareCalculator instance calculates fares consistently.
D. Builder pattern, to construct FareCalculator with complex parameters.

Solution

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

    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 FareCalculator

    FareCalculator needs consistent fare rules and shared state (e.g., pricing configs). Singleton avoids multiple inconsistent instances and ensures thread-safe access.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Private constructor + synchronized getInstance() -> Singleton pattern [check]
Hint: Private constructor + static getInstance() = Singleton [check]
Common Mistakes:
2. In a multi-threaded Chess game server, without synchronizing the makeMove() method, which exact sequence of thread interleaving causes two players to move simultaneously, violating turn order?
medium
A. Thread 1 reads current player as White, moves piece; Thread 2 reads current player as Black, moves piece simultaneously.
B. Thread 1 checks if it's White's turn -> true, context switch; Thread 2 checks if it's White's turn -> true; both proceed to move.
C. Thread 1 locks board, moves piece; Thread 2 waits but proceeds after unlock.
D. Thread 1 updates board state; Thread 2 reads stale board state but waits for turn.

Solution

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

    Both threads read the turn state before either updates it, causing both to believe it's their turn.
  2. Step 2: Trace thread interleaving

    Thread 1 checks turn -> true, context switch before move; Thread 2 checks turn -> true, both move simultaneously, violating turn order.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Check-then-act gap allows double move without synchronization [check]
Hint: Check turn then act without lock -> race condition [check]
Common Mistakes:
3. You want to add a WeekendFeeStrategy to the billing module without modifying existing classes. Which SOLID principle does this design enforce, and what is the minimum change required?
medium
A. Dependency Inversion Principle; inject WeekendFeeStrategy directly into Room class.
B. Open/Closed Principle; create a new WeekendFeeStrategy class implementing FeeStrategy interface, no changes to Billing.
C. Liskov Substitution Principle; subclass Billing to WeekendBilling with weekend fees.
D. Single Responsibility Principle; add weekend fee logic inside existing Billing class.

Solution

  1. Step 1: Identify SOLID principle for extensibility

    Open/Closed Principle (OCP) states software entities should be open for extension but closed for modification.
  2. Step 2: Minimum change to add WeekendFeeStrategy

    Create a new class implementing FeeStrategy and inject it via dependency injection (DIP) into Billing or related classes, avoiding modification of existing code.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Inject new strategy class -> respects OCP and DIP [check]
Hint: Add new strategy class + inject -> OCP and DIP [check]
Common Mistakes:
4. You want to add a WeekendFeeStrategy to calculate notification fees differently on weekends. According to SOLID principles, what is the minimum change required in the current design that uses a FeeStrategy interface?
medium
A. Create a new WeekendFeeStrategy class implementing FeeStrategy and inject it without modifying existing classes.
B. Add a boolean weekend flag to FeeStrategy interface and update all implementations accordingly.
C. Modify the existing FeeStrategy implementation to add weekend logic with if-else checks.
D. Create a WeekendFeeStrategy subclass extending the existing FeeStrategy implementation.

Solution

  1. Step 1: Identify applicable SOLID principle

    The Open-Closed Principle (OCP) requires classes to be open for extension but closed for modification.
  2. Step 2: Evaluate options against OCP

    Create a new WeekendFeeStrategy class implementing FeeStrategy and inject it without modifying existing classes. adds a new class implementing FeeStrategy without modifying existing code, adhering to OCP. Others modify existing classes or interfaces, violating OCP.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    New strategy class extends behavior without modifying existing code [check]
Hint: OCP: extend with new class, don't modify existing [check]
Common Mistakes:
5. The Ride Booking system wants to add a WeekendFeeStrategy for fare calculation. Given the current design uses a FeeStrategy interface, what is the minimum change required to comply with SOLID principles?
medium
A. Modify the existing FareCalculator class to add weekend fee logic inside its calculateFare() method.
B. Add a boolean flag isWeekend to FareCalculator and branch logic accordingly.
C. Create a new WeekendFeeStrategy class implementing FeeStrategy and inject it without modifying existing classes.
D. Create a WeekendFareCalculator subclass extending FareCalculator and override calculateFare().

Solution

  1. Step 1: Identify applicable SOLID principle

    The Open-Closed Principle (OCP) requires classes to be open for extension but closed for modification.
  2. Step 2: Evaluate options against OCP

    Create a new WeekendFeeStrategy class implementing FeeStrategy and inject it without modifying existing classes. adds a new FeeStrategy implementation without modifying existing classes, adhering to OCP. Options A and C modify existing classes, violating OCP. Create a WeekendFareCalculator subclass extending FareCalculator and override calculateFare(). creates subclass but may cause class explosion.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    New FeeStrategy implementation -> OCP compliant [check]
Hint: New strategy class, no existing class changes [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