Bird
Raised Fist0

Design an Online Tic-Tac-Toe Game - Board, Player, Win Detection, Replay

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

Player is an abstract class holding state id and symbol, which interfaces cannot hold in Java.

🎯 Tests whether candidate knows abstract class is needed over interface because Player has instance fields id and symbol.
public abstract class Player {
    protected String id;
    protected char symbol;
    public String getId() { return id; }
    public char getSymbol() { return symbol; }
}
Board composition of Cells

Board composes a 2D array of Cells, showing composition with lifecycle ownership of Cells.

🎯 Tests whether candidate understands composition means Board owns Cells lifecycle and must manage their state.
public class Board {
    private Cell[][] cells;
    public synchronized boolean markCell(int row, int col, char symbol) {
        if (cells[row][col].isEmpty()) {
            cells[row][col].setSymbol(symbol);
            return true;
        }
        return false;
    }
}
GameObserver interface and realization

GameObserver is an interface realized by classes that observe game events, enabling OCP for adding observers.

🎯 Tests whether candidate knows interface is used here for behavior abstraction without state, enabling multiple implementations.
public interface GameObserver {
    void onGameEnd(Game game);
}
GameManager Singleton with volatile

GameManager is a Singleton with volatile instance and synchronized getInstance() to ensure safe lazy initialization.

🎯 Tests whether candidate understands volatile prevents instruction reordering and synchronized ensures thread-safe singleton instantiation.
public class GameManager {
    private static volatile GameManager instance;
    private GameManager() {}
    public static synchronized GameManager getInstance() {
        if (instance == null) {
            instance = new GameManager();
        }
        return instance;
    }
}
Game composition and synchronized playTurn

Game composes Board and Players, and playTurn is synchronized to atomically update game state and notify observers.

🎯 Tests whether candidate knows synchronized method ensures atomic state changes and safe observer notifications in concurrent play.
public class Game {
    private Board board;
    private Player[] players;
    private List<GameObserver> observers;
    public synchronized boolean playTurn(int row, int col) {
        // mark cell and check win
        // notify observers if game ends
        return true;
    }
    public void addObserver(GameObserver observer) {
        observers.add(observer);
    }
}
📐
Class Diagram
Class Diagram
       +----------------+
       | <<Abstract>>   |
       | Player         |
       +----------------+
       | -id: String    |
       | -symbol: char  |
       +----------------+
       | +getId(): String|
       | +getSymbol():char|
       +----------------+
              │
              │
       +----------------+
       | Board          |
       +----------------+
       | -cells: Cell[][]|
       +----------------+
       | +markCell(int,int,char):bool [sync]|
       | +isFull():bool                 |
       +----------------+
              ◆
              │ composition
              │
       +----------------+
       | Cell           |
       +----------------+
       | -row: int      |
       | -col: int      |
       | -symbol: char  |
       +----------------+
       | +isEmpty():boolean|
       | +getSymbol():char |
       +----------------+
              │
              │ association
              │
       +----------------+
       | <<Interface>>  |
       | GameObserver   |
       +----------------+
       | +onGameEnd(Game):void|
       +----------------+
              ─ ─ ─►
              realization
              │
       +----------------+
       | GameManager    |
       | <<Singleton>>  |
       +----------------+
       | -instance: volatile GameManager|
       +----------------+
       | +getInstance(): GameManager [sync]|
       | +startGame(Player,Player): Game|
       +----------------+
              ◆
              │ composition
              │
       +----------------+
       | Game           |
       +----------------+
       | -board: Board  |
       | -players: Player[]|
       | -observers: List<GameObserver>|
       +----------------+
       | +playTurn(int,int): boolean [sync]|
       | +addObserver(GameObserver): void|
       +----------------+

This diagram highlights four key Java-specific design decisions in the Online Tic-Tac-Toe system. First, Player is an abstract class because it holds instance state like id and symbol, which interfaces cannot hold in Java. Second, Board composes Cells, indicating lifecycle ownership; Cells cannot exist independently, enforcing encapsulation. Third, GameObserver is an interface realized by observers, enabling open-closed principle by allowing new observers without modifying Game. Fourth, GameManager is a thread-safe Singleton using volatile and synchronized getInstance() to prevent instruction reordering and ensure safe lazy initialization. Finally, Game composes Board and Players and uses synchronized playTurn to atomically update game state and notify observers, preventing race conditions in concurrent play.

Primary Focus

✓ Composition lifecycle + Interface realization + Abstract class state + Singleton volatile

Practice

(1/5)
1. In the Online Shopping Cart system, the Cart 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 controls object creation centrally.
B. Singleton pattern, to ensure only one Cart instance exists per user session.
C. Builder pattern, to construct Cart objects step-by-step.
D. Prototype pattern, to clone Cart instances efficiently.

Solution

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

    The private constructor prevents external instantiation, and the static synchronized getInstance() method controls instance creation, which matches the Singleton pattern.
  2. Step 2: Reason why Singleton fits Cart

    Ensuring only one Cart instance per user session avoids inconsistent states and duplicate carts, which is the intended use case here.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Private constructor + synchronized getInstance() -> Singleton pattern [check]
Hint: Private constructor + static 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? Why?
easy
A. Interface, because it enforces method signatures without implementation or state, which is ideal for all vehicles.
B. Abstract class, because it forces all subclasses to implement all methods explicitly.
C. Interface, because Java 8+ allows default methods that can hold state variables safely.
D. Abstract class, because it can hold shared state like licensePlate and provide common behavior implementations.

Solution

  1. Step 1: Identify state holding needs

    licensePlate is shared state, which interfaces cannot hold as fields.
  2. Step 2: Evaluate options

    Abstract classes can hold state and provide default behavior; interfaces cannot hold instance fields, only constants.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Abstract class holds shared state like licensePlate [check]
Hint: State needs abstract class, interfaces only declare behavior [check]
Common Mistakes:
3. In the Ride Booking system, the parkVehicle() method checks if a parking spot is available and then marks it occupied. Without synchronization, which exact thread interleaving causes double-booking of the same spot?
medium
A. Thread 1 checks isAvailable() -> true, context switch before marking occupied; Thread 2 checks isAvailable() -> true, both mark occupied.
B. Thread 1 reads spot as occupied, Thread 2 reads spot as free, both park simultaneously.
C. Thread 1 marks spot occupied, Thread 2 waits and then parks after spot is freed.
D. Thread 1 and Thread 2 both wait on a lock, preventing double-booking.

Solution

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

    Without synchronization, Thread 1 reads spot as free (isAvailable() -> true) but is preempted before marking it occupied.
  2. Step 2: Thread 2 also reads spot as free and proceeds to mark it occupied, causing double-booking.

    This is the classic check-then-act gap causing race conditions.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Unsynchronized check-then-act -> double-booking race [check]
Hint: Check-then-act without sync -> double-booking race [check]
Common Mistakes:
4. In a multithreaded LRU Cache, if the put() method is not synchronized, which exact sequence of operations between Thread 1 and Thread 2 can cause the cache to contain duplicate keys or corrupt the doubly linked list?
medium
A. Thread 1 and Thread 2 both call get() simultaneously, causing stale reads of the cache order.
B. Thread 1 updates the value of an existing key while Thread 2 evicts a different key, causing no race condition.
C. Thread 1 removes the tail node while Thread 2 updates the head node, causing inconsistent pointers but no duplicate keys.
D. Thread 1 checks if key exists -> false, context switch; Thread 2 checks if key exists -> false; both insert new nodes for the same key concurrently.

Solution

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

    Without synchronization, two threads can both check for key existence and find it missing before either inserts, leading to duplicate nodes for the same key.
  2. Step 2: Trace the exact interleaving

    Thread 1: check key -> false; context switch; Thread 2: check key -> false; both insert new nodes, corrupting the cache state.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Concurrent check-then-act without lock -> duplicate keys inserted [check]
Hint: Check-then-act without lock causes duplicates [check]
Common Mistakes:
5. You want to add a WeekendFeeStrategy to the parking lot pricing system. Which minimum change respects SOLID principles?
medium
A. Add a boolean flag isWeekend to Vehicle and adjust pricing accordingly.
B. Create a new WeekendFeeStrategy class implementing FeeStrategy and inject it without changing existing classes.
C. Modify the existing FeeCalculator class to add weekend logic inside.
D. Create a subclass WeekendFeeCalculator extending FeeCalculator and override methods.

Solution

  1. Step 1: Identify SOLID principle involved

    Open-Closed Principle (OCP) requires adding new functionality without modifying existing code.
  2. Step 2: Evaluate options

    Modify the existing FeeCalculator class to add weekend logic inside. violates OCP by modifying existing class. Create a new WeekendFeeStrategy class implementing FeeStrategy and inject it without changing existing classes. respects OCP by adding new strategy class. Add a boolean flag isWeekend to Vehicle and adjust pricing accordingly. incorrectly mixes state into Vehicle, violating SRP. Create a subclass WeekendFeeCalculator extending FeeCalculator and override methods. adds subclass but may cause class explosion and tight coupling.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    New strategy class + no existing class change -> OCP respected [check]
Hint: Add new strategy class, avoid modifying existing code [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