Bird
Raised Fist0

Wallet System OOP - Account, Transaction, Transfer, Ledger, Statement

LLDmediumInterview freq: medium
Choose your preparation mode4 modes available
▶
Class Trace
Step 1/4
Account abstract class

Account is an abstract class holding shared state like accountId and balance with synchronized methods to ensure thread-safe balance updates.

🎯 Tests whether candidate understands abstract class is used because Account holds mutable state (balance) which interfaces cannot hold in Java, and synchronized methods prevent race conditions on balance updates.
public abstract class Account {
    protected String accountId;
    protected volatile double balance;
    public synchronized double getBalance() {
        return balance;
    }
    public synchronized void credit(double amount) {
        balance += amount;
    }
    public synchronized void debit(double amount) {
        balance -= amount;
    }
}
Transfer class extends Account

Transfer extends Account to represent a transaction moving funds between two accounts, with execute() synchronized to atomically debit and credit involved accounts.

🎯 Tests whether candidate knows inheritance models 'is-a' relationship and synchronized execute() prevents interleaving where partial transfer could occur causing inconsistent balances.
public class Transfer extends Account {
    private Account fromAccount;
    private Account toAccount;
    private double amount;
    public synchronized void execute() {
        fromAccount.debit(amount);
        toAccount.credit(amount);
    }
}
Ledger composition with Transactions

Ledger composes multiple Transaction objects (including Transfer) representing the transaction history, owning their lifecycle strongly.

🎯 Tests whether candidate distinguishes composition (Ledger owns Transactions lifecycle) from association, enforcing Single Responsibility Principle by separating transaction storage from account logic.
public class Ledger {
    private List<Transaction> transactions = new ArrayList<>();
    public void addTxn(Transaction txn) {
        transactions.add(txn);
    }
    public List<Transaction> getTxns() {
        return Collections.unmodifiableList(transactions);
    }
}
Statement association with Ledger

Statement holds a reference to Ledger via association, generating reports without owning Ledger lifecycle.

🎯 Tests whether candidate understands association means Statement depends on Ledger but does not control its lifecycle, supporting Open-Closed Principle by allowing Statement to extend reporting without modifying Ledger.
public class Statement {
    private Ledger ledger;
    public Statement(Ledger ledger) {
        this.ledger = ledger;
    }
    public void generate() {
        // generate report from ledger.getTxns()
    }
}
📐
Class Diagram
Class Diagram
       +----------------+
       | <<Abstract>>   |
       | Account        |
       +----------------+
       | - accountId    |
       | - balance      |
       +----------------+
       | + getBalance() |
       | + credit()     |
       | + debit()      |
       | [sync]         |
       +----------------+
              │ extends
       +----------------+
       | Transfer       |
       +----------------+
       | - fromAccount  |
       | - toAccount    |
       | - amount       |
       +----------------+
       | + execute()    |
       | [sync]         |
       +----------------+
              ◆ composition
       +----------------+
       | Ledger         |
       +----------------+
       | - txns         |
       +----------------+
       | + addTxn()     |
       | + getTxns()    |
       +----------------+
              ──► association
       +----------------+
       | Statement      |
       +----------------+
       | - ledger       |
       +----------------+
       | + generate()   |
       +----------------+

This Wallet System design uses an abstract Account class to hold shared mutable state like balance, requiring synchronized methods to prevent race conditions in concurrent updates. Transfer extends Account to model fund transfers with atomic execute() method synchronized to avoid partial state changes. Ledger composes Transaction objects, owning their lifecycle to maintain transaction history, enforcing strong encapsulation and Single Responsibility Principle. Statement associates with Ledger to generate reports without owning Ledger lifecycle, supporting Open-Closed Principle by allowing extension without modification. The design carefully uses Java-specific constructs like volatile for balance visibility and synchronized methods for atomicity, demonstrating precise concurrency control and clear lifecycle ownership.

Primary Focus

✓ Composition lifecycle + Abstract class with state + synchronized methods + Association vs Composition

Practice

(1/5)
1. A RateLimiter class has a private constructor and a static synchronized getInstance() method returning a single instance. Which design pattern is applied and why is this the correct choice for a token bucket rate limiter?
easy
A. Builder pattern to configure token bucket parameters fluently
B. Factory pattern to create multiple rate limiter instances with different limits
C. Singleton pattern to ensure a single shared token bucket across threads
D. Prototype pattern to clone rate limiter instances for each client

Solution

  1. Step 1: Identify the pattern from constructor and getInstance()

    The private constructor and static synchronized getInstance() method indicate Singleton pattern, ensuring only one instance exists.
  2. Step 2: Why Singleton fits token bucket rate limiter

    Singleton ensures a single shared token bucket state across threads, preventing inconsistent token counts.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Singleton enforces single shared instance for thread-safe token bucket [check]
Hint: Private constructor + static synchronized getInstance() -> Singleton [check]
Common Mistakes:
2. In designing a Vehicle abstraction for a parking system, should Vehicle be an abstract class or an interface if it needs to hold state like licensePlate and provide default behavior for getSize()? Why?
easy
A. Abstract class, because it can hold shared state and provide default method implementations.
B. Interface, because it allows multiple inheritance and enforces method contracts without state.
C. Interface, because Java 8+ interfaces can have default methods and fields for state.
D. Abstract class, because it forces all subclasses to implement all methods explicitly.

Solution

  1. Step 1: Analyze state and behavior requirements

    Vehicle needs to hold state (licensePlate) and provide default behavior (getSize), which interfaces cannot hold as fields.
  2. Step 2: Choose abstraction supporting state and default behavior

    Abstract classes support instance variables and method implementations; interfaces cannot hold instance state.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Abstract class supports state and default behavior -> correct choice [check]
Hint: State needs abstract class; interfaces can't hold instance fields [check]
Common Mistakes:
3. The Food Ordering System needs to add a WeekendFeeStrategy to apply extra charges on weekends. Given the current design uses a FeeStrategy interface for pricing, what is the minimum change required to add this feature while respecting SOLID principles?
medium
A. Add a boolean weekend flag to the Order class and update all pricing logic accordingly.
B. Create a new WeekendFeeStrategy class implementing FeeStrategy and inject it where needed without modifying existing classes.
C. Combine weekend fee logic inside the OrderManager class to centralize pricing.
D. Modify the existing FeeStrategy implementation to add weekend logic with if-else checks.

Solution

  1. Step 1: Identify the SOLID principle involved

    The Open-Closed Principle (OCP) requires that existing code should not be modified to add new behavior.
  2. Step 2: Evaluate options against OCP

    Create a new WeekendFeeStrategy class implementing FeeStrategy and inject it where needed without modifying existing classes. respects OCP by adding a new strategy class without modifying existing ones. Add a boolean weekend flag to the Order class and update all pricing logic accordingly. modifies Order class and pricing logic, violating OCP. Modify the existing FeeStrategy implementation to add weekend logic with if-else checks. and D modify existing classes, violating OCP.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    New strategy class -> OCP respected, no existing class changes [check]
Hint: New strategy class -> respect OCP, avoid modifying existing code [check]
Common Mistakes:
4. In the messaging system, the DeliveryStatus class has a method markDelivered() that updates the delivery state. Without synchronizing this method, which exact thread interleaving causes inconsistent delivery status updates?
medium
A. Thread 1 reads status as undelivered, context switch; Thread 2 reads undelivered, both set delivered, overwriting each other's update.
B. Thread 1 calls markDelivered() while Thread 2 calls markFailed(), causing deadlock.
C. Thread 1 updates status to delivered, but Thread 2 never sees the update due to caching.
D. Thread 1 and Thread 2 both call markDelivered() simultaneously, causing a crash.

Solution

  1. Step 1: Identify the race condition in markDelivered()

    Without synchronization, two threads can read the undelivered status before either writes the delivered status.
  2. Step 2: Trace the interleaving causing lost update

    Thread 1 reads undelivered -> context switch -> Thread 2 reads undelivered -> both write delivered, one overwriting the other's update.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Check-then-act without sync -> lost update race condition [check]
Hint: Check-then-act gap -> lost update race [check]
Common Mistakes:
5. Electric cars require charging spots in the parking module. What is the minimum change to the current Java design to support ElectricCar and ChargingSpot without modifying existing classes?
hard
A. Add ELECTRIC_CAR to VehicleType enum, CHARGING to SpotType, create ElectricCar subclass, and update compatibility map externally.
B. Modify Vehicle class to add an isElectric flag and update spot compatibility logic inside existing classes.
C. Create a new ElectricVehicle interface and make ElectricCar implement it, modifying Spot to check for this interface.
D. Add charging logic inside existing ParkingLot class and extend Spot with charging capability.

Solution

  1. Step 1: Identify extensibility without modifying existing classes

    Adding new enum values and subclasses allows extension without changing existing class code.
  2. Step 2: Update compatibility map externally to maintain open-closed principle and avoid class explosion.

    Options B, C, and D require modifying existing classes, violating OCP or causing tight coupling.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    New enums + subclass + external compatibility update -> minimal change [check]
Hint: Add enums + subclass + external map update, no existing class 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