Bird
Raised Fist0

Design a Messaging System OOP - User, Conversation, Message, Delivery Status

LLDmediumInterview freq: high
Choose your preparation mode4 modes available
▶
Class Trace
Step 1/4
User-Conversation composition

User class composes Conversation objects, indicating User owns the lifecycle of Conversations.

🎯 Tests understanding of composition in Java where User holds strong references to Conversations, enforcing lifecycle dependency.
public class User {
    private String userId;
    private List<Conversation> conversations = new ArrayList<>();
    public void addConversation(Conversation c) {
        conversations.add(c);
    }
}
Conversation-Message composition and synchronized addMessage

Conversation composes Messages and uses synchronized addMessage() to ensure thread-safe mutation of messages list.

🎯 Tests candidate's knowledge that synchronized methods in Java prevent race conditions on shared mutable state like messages list.
public class Conversation {
    private List<Message> messages = new ArrayList<>();
    public synchronized void addMessage(Message m) {
        messages.add(m);
    }
}
DeliveryStatusTracker Singleton with volatile instance

DeliveryStatusTracker is a Singleton with volatile instance and synchronized getInstance() to prevent unsafe publication.

🎯 Tests understanding of volatile keyword in Java to avoid half-constructed object visibility and synchronized method to ensure thread-safe lazy init.
public class DeliveryStatusTracker {
    private static volatile DeliveryStatusTracker instance;
    private Map<String, String> statusMap = new HashMap<>();
    private DeliveryStatusTracker() {}
    public static synchronized DeliveryStatusTracker getInstance() {
        if (instance == null) {
            instance = new DeliveryStatusTracker();
        }
        return instance;
    }
}
Message class state and behavior

Message holds immutable state like messageId, content, and timestamp, encapsulating message data and behavior.

🎯 Tests candidate's ability to design domain entities with encapsulated state and methods for behavior like editContent() and getStatus().
public class Message {
    private String messageId;
    private String content;
    private long timestamp;
    public void editContent(String newContent) {
        this.content = newContent;
    }
}
📐
Class Diagram
Class Diagram
       +----------------+
       |     User       |
       +----------------+
       | -userId: String|
       | -name: String  |
       +----------------+
       | +sendMessage() |
       | +receiveMessage()|
       +----------------+
              │
              │ ◆ composition (User owns Conv. lifecycle)
              ▼
       +---------------------+
       |   Conversation      |
       +---------------------+
       | -conversationId: String|
       | -messages: List<Message>|
       +---------------------+
       | +addMessage() [sync] |
       | +getMessages()       |
       +---------------------+
              │
              │ ◆ composition (Conv. owns Msg lifecycle)
              ▼
       +----------------+
       |    Message     |
       +----------------+
       | -messageId: String|
       | -content: String |
       | -timestamp: long |
       +----------------+
       | +editContent()  |
       | +getStatus()    |
       +----------------+
              │
              │ ─ ─ ─► realization (DeliveryStatusTracker tracks status)
              ▼
       +-------------------------+
       | DeliveryStatusTracker   |
       | <<Singleton>>           |
       +-------------------------+
       | -instance: volatile DeliveryStatusTracker|
       | -statusMap: Map<String, String>|
       +-------------------------+
       | +getInstance() [sync]    |
       | +updateStatus() [sync]   |
       | +getStatus()             |
       +-------------------------+

This class diagram illustrates key Java OOP design decisions in a messaging system. User composes Conversations, enforcing lifecycle ownership via composition. Conversation composes Messages and uses synchronized addMessage() to ensure thread-safe updates to its message list, preventing race conditions. DeliveryStatusTracker is implemented as a Singleton with a volatile instance field and synchronized getInstance() method to guarantee safe lazy initialization and visibility across threads. Message encapsulates immutable state and behavior, demonstrating encapsulation and domain modeling. These design choices enforce SOLID principles, especially Single Responsibility and Dependency Inversion, while leveraging Java-specific concurrency constructs like volatile and synchronized for thread safety.

Primary Focus

✓ Composition vs Association lifecycle + Abstract class state + Singleton volatile + Synchronized methods concurrency

Practice

(1/5)
1. In the Restaurant Management System, the OrderManager class has a private constructor and a static synchronized getInstance() method. Which design pattern is applied here and why is it the correct choice?
easy
A. Factory pattern, because it controls object creation centrally.
B. Builder pattern, to incrementally construct complex orders.
C. Singleton pattern, to ensure only one instance manages all orders globally.
D. Prototype pattern, to clone existing orders 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, which matches the Singleton pattern.
  2. Step 2: Why Singleton fits OrderManager

    OrderManager must coordinate all orders centrally to avoid conflicting states; multiple instances would cause inconsistent order tracking.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Private constructor + synchronized static getInstance() -> Singleton [check]
Hint: Private constructor + static getInstance() -> Singleton [check]
Common Mistakes:
2. In a Snake and Ladder game design, the Board class has a private constructor and a static synchronized getInstance() method. Which design pattern is applied here and why is it the correct choice?
easy
A. Singleton pattern, to ensure only one board instance exists during the game.
B. Factory pattern, because it controls object creation centrally.
C. Builder pattern, to incrementally construct the board with snakes and ladders.
D. Prototype pattern, to clone the board for multiple game sessions.

Solution

  1. Step 1: Identify the role of private constructor and static synchronized method

    The private constructor prevents external instantiation, and the synchronized getInstance() ensures thread-safe single instance creation.
  2. Step 2: Match to design pattern intent

    Singleton pattern ensures exactly one Board instance exists, which is critical to maintain consistent game state.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Singleton controls single instance creation with thread safety [check]
Hint: Private constructor + static getInstance() = Singleton pattern [check]
Common Mistakes:
3. In a multi-threaded notification system, the sendNotification() method checks if a channel is available before sending. Without synchronizing this method, what exact sequence of thread interleaving causes duplicate notifications to be sent?
medium
A. Thread 1 updates channel status after sending; Thread 2 reads stale status and retries sending.
B. Thread 1 sends notification; Thread 2 waits until Thread 1 finishes sending.
C. Thread 1 and Thread 2 both block on a lock and then send sequentially.
D. Thread 1 checks isAvailable() -> true, context switch; Thread 2 checks isAvailable() -> true; both send notifications concurrently.

Solution

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

    Without synchronization, both threads can see the channel as available before either marks it busy.
  2. Step 2: Trace thread interleaving

    Thread 1 checks availability -> true, context switch; Thread 2 checks availability -> true; both proceed to send, causing duplicate notifications.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Unsynchronized check-then-act allows concurrent sends [check]
Hint: Check-then-act without lock -> race, double send [check]
Common Mistakes:
4. To add a WeekendFeeStrategy for weekend parking fees without modifying existing classes, which SOLID principle is being applied and what is the minimum required change?
medium
A. Single Responsibility Principle: modify ParkingLot to handle weekend fees directly.
B. Dependency Inversion Principle: hardcode weekend fee logic inside ParkingLot's fee calculation.
C. Open/Closed Principle: create a new WeekendFeeStrategy class implementing FeeStrategy; inject it without changing ParkingLot.
D. Liskov Substitution Principle: subclass ParkingLot to override fee calculation for weekends.

Solution

  1. Step 1: Identify SOLID principle for extensibility

    Open/Closed Principle states software entities should be open for extension but closed for modification.
  2. Step 2: Apply OCP by adding new WeekendFeeStrategy implementing FeeStrategy interface without modifying ParkingLot.

  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    New strategy class added, existing classes unchanged -> OCP respected [check]
Hint: OCP: extend with new class, don't modify existing ones [check]
Common Mistakes:
5. ElectricCar requires ChargingSpots in the parking lot system. What is the minimum change to support this without modifying existing classes?
hard
A. Add charging logic inside existing Spot class with conditional checks for electric vehicles.
B. Modify Vehicle class to include a boolean isElectric field and add charging logic inside existing methods.
C. Add ELECTRIC_CAR to VehicleType enum, CHARGING to SpotType enum, create ElectricCar subclass, and update SpotCompatibility map.
D. Create a new ChargingSpot class extending Spot and modify ParkingLot to handle it explicitly.

Solution

  1. Step 1: Identify extensibility requirements

    Supporting ElectricCar and ChargingSpots requires new types without changing existing class logic to respect OCP.
  2. Step 2: Apply minimal change approach

    Adding new enum values and subclass ElectricCar allows extension without modifying existing classes. Updating SpotCompatibility map centralizes compatibility logic.
  3. Step 3: Eliminate options violating OCP

    Options B, C, and D modify existing classes or add conditional logic, violating OCP and risking class explosion or god classes.
  4. Final Answer:

    Option C -> Option C
  5. Quick Check:

    New enums + subclass + compatibility map update -> minimal, OCP-compliant [check]
Hint: Add enums + subclass + compatibility map, 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