Bird
Raised Fist0

Design a Card Game Framework - Deck, Card, Hand, Rules Engine, Multiplayer

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

Card is an abstract class holding suit and rank state, which interfaces cannot hold in Java.

🎯 Tests whether candidate knows abstract class is needed to hold shared instance state (suit, rank) that interfaces cannot hold in Java.
public abstract class Card {
    private final String suit;
    private final String rank;
    public Card(String suit, String rank) {
        this.suit = suit;
        this.rank = rank;
    }
    public String getSuit() { return suit; }
    public String getRank() { return rank; }
}
Deck composition with Card

Deck has a composition relationship with Card list, meaning Deck owns Cards lifecycle and manages them internally.

🎯 Tests whether candidate distinguishes composition (Deck owns Cards) from association or aggregation, enforcing strong lifecycle ownership.
public class Deck {
    private final List<Card> cards = new ArrayList<>();
    public void shuffle() {
        Collections.shuffle(cards);
    }
    public Card drawCard() {
        if (cards.isEmpty()) return null;
        return cards.remove(cards.size() - 1);
    }
}
Hand composition with Card

Hand also composes Cards, representing a player's current cards with independent lifecycle from Deck.

🎯 Tests understanding of composition lifecycle: Hand owns its Cards independently from Deck, allowing cards to move between them.
public class Hand {
    private final List<Card> cards = new ArrayList<>();
    public void addCard(Card card) {
        cards.add(card);
    }
    public void removeCard(Card card) {
        cards.remove(card);
    }
}
RulesEngine Singleton with volatile

RulesEngine is a thread-safe singleton using volatile instance and synchronized getInstance() to prevent half-constructed instance visibility.

🎯 Tests whether candidate knows volatile prevents JVM reordering and ensures safe publication of singleton instance in Java concurrency.
public class RulesEngine {
    private static volatile RulesEngine instance;
    private RulesEngine() {}
    public static synchronized RulesEngine getInstance() {
        if (instance == null) {
            instance = new RulesEngine();
        }
        return instance;
    }
    public boolean validateMove() {
        // validation logic
        return true;
    }
}
📐
Class Diagram
Class Diagram
       +----------------+
       |    <<Abstract>>|
       |      Card      |
       +----------------+
       | - suit: String |
       | - rank: String |
       +----------------+
       | + getSuit():String
       | + getRank():String
       +----------------+
              │
              │
       +----------------+
       |      Deck      |
       +----------------+
       | - cards: List<Card> ◆
       +----------------+
       | + shuffle():void
       | + drawCard():Card
       +----------------+
              │
              │
       +----------------+
       |      Hand      |
       +----------------+
       | - cards: List<Card> ◆
       +----------------+
       | + addCard(Card):void
       | + removeCard(Card):void
       +----------------+
              │
              │
       +----------------+
       | <<Singleton>>  |
       |  RulesEngine   |
       +----------------+
       | - instance: volatile RulesEngine
       +----------------+
       | + getInstance():RulesEngine [sync]
       | + validateMove():boolean
       +----------------+

This class diagram illustrates key design decisions in a Card Game Framework. Card is an abstract class holding immutable state (suit and rank), which interfaces cannot hold in Java, enforcing shared state encapsulation. Deck and Hand both compose Card lists, showing strong lifecycle ownership where Deck manages the full card set and Hand manages a player's subset independently. RulesEngine is implemented as a thread-safe singleton using a volatile instance field and synchronized getInstance() method to prevent concurrency bugs like half-constructed instances being visible to other threads. These decisions enforce SOLID principles: single responsibility for Card, open-closed for RulesEngine extensibility, and strong encapsulation via composition. The diagram and trace test understanding of Java-specific concurrency, lifecycle ownership, and abstract class usage.

Primary Focus

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

Practice

(1/5)
1. A class CarBuilder constructs a Car object step-by-step using methods like setEngine(), setWheels(), and finally build(). The Car constructor is private. Which design pattern is applied here and why is it the correct choice?
easy
A. Builder pattern, because it separates complex object construction from its representation, allowing stepwise creation.
B. Factory Method pattern, because it creates objects through inheritance and subclasses.
C. Prototype pattern, because it clones existing objects to create new ones efficiently.
D. Singleton pattern, because it ensures only one instance of Car exists.

Solution

  1. Step 1: Identify the construction process

    The class uses multiple setter methods to configure parts of the object before finalizing with build(), indicating stepwise construction.
  2. Step 2: Match pattern intent

    Builder pattern separates construction from representation, ideal for complex objects with many optional parts. Factory Method uses inheritance, Prototype clones objects, Singleton restricts instance count.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Stepwise object creation with private constructor -> Builder pattern [check]
Hint: Stepwise construction with private constructor -> Builder pattern [check]
Common Mistakes:
2. In designing a plugin interface in Java, should the Plugin be an abstract class or an interface if plugins must maintain internal state and provide default lifecycle methods? Why?
easy
A. Interface, because it allows multiple inheritance and enforces method signatures without state.
B. Interface, because Java interfaces can now have private fields for state.
C. Abstract class, because it can hold shared state and provide default method implementations.
D. Abstract class, because it forces all plugins to share the same constructor signature.

Solution

  1. Step 1: Analyze state and default lifecycle method needs

    Interfaces cannot hold instance state; abstract classes can hold fields and provide default method bodies.
  2. Step 2: Determine which construct fits stateful plugins with default behavior

    Abstract class fits because it supports shared state and default lifecycle implementations, easing plugin development.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Abstract class supports state and default methods [check]
Hint: State + default methods = abstract class, not interface [check]
Common Mistakes:
3. In designing the vending machine's product selection module, should Product be modeled as an abstract class or an interface? Consider that products have shared state like price and inventory count, but different behaviors like dispense() and getDescription().
easy
A. Abstract class, because it allows sharing common state and default behavior implementations among products.
B. Abstract class, because it enforces immutability of product instances.
C. Interface, because Java interfaces can now have default methods to share code and state.
D. Interface, because all products only share behavior signatures without any shared state.

Solution

  1. Step 1: Analyze shared state and behavior

    Products share common state like price and inventory count, which requires fields and possibly default implementations.
  2. Step 2: Choose between interface and abstract class

    Abstract classes can hold state and provide default method implementations, while interfaces cannot hold instance state (only constants).
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Abstract class supports shared state and behavior [check]
Hint: Abstract class holds state; interface only behavior signatures [check]
Common Mistakes:
4. In designing a collection class that supports iteration, should the iterator interface be an abstract class or an interface in Java, and why?
easy
A. Interface, because it enforces immutability of the collection during iteration.
B. Abstract class, because it allows sharing state and default implementation for iteration methods.
C. Abstract class, because Java interfaces cannot have methods with parameters.
D. Interface, because it defines behavior without state, allowing multiple implementations and better flexibility.

Solution

  1. Step 1: Understand Java interface vs abstract class

    Interfaces define behavior contracts without state, allowing multiple implementations. Abstract classes can hold state but restrict multiple inheritance.
  2. Step 2: Apply to iterator design

    Iterator interface defines traversal methods (e.g., hasNext(), next()) without state, enabling different iterator implementations per collection type.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Iterator as interface enables flexible, stateless traversal contracts [check]
Hint: Iterator interface defines behavior, no state needed [check]
Common Mistakes:
5. To support ElectricCar needing ChargingSpot in the vending machine parking design, what is the minimum change that respects extensibility without modifying existing classes?
hard
A. Create a new ChargingSpot class extending Spot and modify ParkingLot to handle it specially.
B. Add ELECTRIC_CAR to VehicleType enum, CHARGING to SpotType enum, create ElectricCar subclass, and update SpotCompatibility.COMPATIBILITY_MAP accordingly.
C. Modify existing Vehicle and Spot classes to add electric-specific fields and logic.
D. Add a boolean isElectric flag to Vehicle and Spot classes and add conditional logic everywhere.

Solution

  1. Step 1: Identify extensibility requirements

    Adding new vehicle and spot types without modifying existing classes preserves OCP and avoids class explosion.
  2. Step 2: Evaluate options for minimal invasive change

    Add ELECTRIC_CAR to VehicleType enum, CHARGING to SpotType enum, create ElectricCar subclass, and update SpotCompatibility.COMPATIBILITY_MAP accordingly. adds new enum values and a subclass, updating compatibility map without changing existing classes. Others modify existing classes or add flags causing complexity.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    New enums and subclass extend design without modifying existing classes [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