Bird
Raised Fist0

Design a Music Streaming Library - Tracks, Albums, Playlists, Playback Queue

LLDmediumInterview freq: medium
Choose your preparation mode4 modes available
🎯
Design a Music Streaming Library - Tracks, Albums, Playlists, Playback Queue
40 min · medium
medium

Design a music streaming library system that manages Tracks, Albums, Playlists, and a Playback Queue. The system should support adding/removing tracks, organizing tracks into albums and playlists, and managing playback order with concurrency safety.

  • Tracks have metadata: title, artist, duration, and unique trackId.
  • Albums are collections of Tracks and have album-level metadata.
  • Playlists are user-defined ordered collections of Tracks and can be modified concurrently.
  • Playback Queue manages the order of tracks to be played and supports concurrent access from multiple threads.
📋
Guided Steps
1/5
Step 1
Define Track and Album classes with proper state encapsulation
Design Track and Album classes capturing metadata and relationships. Decide whether to use abstract classes or interfaces for Track.
💡 Hint
Use abstract class for Track if it holds instance state; interfaces cannot hold fields in Java.
abstract class Track(trackId, title, artist, duration) - abstract because it holds instance state; interface cannot hold fields in Java
Mid: Identifies abstract class for shared state and behavior. Senior: Explains why interface cannot hold instance fields and how abstract class supports code reuse.
⚠️Common Wrong Answer
Using interface for Track with fields leads to inability to hold state, forcing redundant fields in subclasses and violating DRY.
Step 2
Design Playlist with thread-safe modification methods
Implement Playlist class supporting concurrent add/remove of tracks. How to ensure thread safety for internal track list?
💡 Hint
Use synchronized methods or concurrent collections to protect internal state during modifications.
public synchronized boolean addTrack(Track t) and public synchronized boolean removeTrack(Track t) - synchronized to atomically modify internal track list and prevent race conditions
Mid: Uses synchronized methods for thread safety. Senior: Explains exact thread interleaving causing lost updates if synchronization is missing.
⚠️Common Wrong Answer
Using unsynchronized add/remove leads to race conditions where two threads concurrently modify the playlist causing lost updates or corrupted internal state.
Step 3
Implement PlaybackQueue with volatile and synchronized for concurrency
Design PlaybackQueue to manage current playing track and queue order with concurrent access. How to ensure visibility and atomicity?
💡 Hint
Use volatile for currentTrack reference and synchronized for queue modifications to ensure visibility and atomicity.
volatile Track currentTrack - ensures visibility of current playing track across threads; synchronized methods for enqueue/dequeue to atomically update queue
Mid: Uses volatile and synchronized correctly. Senior: Explains how without volatile, thread may see stale currentTrack; without synchronized, queue corruption or lost updates occur.
⚠️Common Wrong Answer
Not using volatile for currentTrack causes threads to see stale references; not synchronizing queue methods causes race conditions corrupting playback order.
Step 4
Model relationships between Albums, Tracks, and Playlists with proper lifecycle ownership
Define relationships between Album, Track, and Playlist classes. Which are composition vs aggregation? Can Tracks exist independently?
💡 Hint
Tracks exist independently; Albums aggregate Tracks; Playlists aggregate Tracks. Use aggregation for shared lifecycle, composition if lifecycle is bound.
Album aggregates Tracks (aggregation) - Tracks can exist independently; Playlist aggregates Tracks (aggregation) - Tracks can be shared; no composition to avoid tight lifecycle coupling
Mid: Correctly identifies aggregation vs composition. Senior: Explains lifecycle ownership and why composition would wrongly tie Track lifecycle to Album or Playlist.
⚠️Common Wrong Answer
Using composition for Album->Track causes Tracks to be destroyed with Album, preventing reuse in Playlists and violating single responsibility.
Step 5
Use enums for TrackGenre and PlaybackState to avoid runtime errors
Add genre and playback state management. How to represent these types safely in Java?
💡 Hint
Use enum for TrackGenre and PlaybackState to get compile-time safety and exhaustive switch support.
enum TrackGenre {POP, ROCK, JAZZ, CLASSICAL} and enum PlaybackState {PLAYING, PAUSED, STOPPED} - enums provide type safety and prevent runtime typo bugs
Mid: Uses enums for fixed sets. Senior: Explains how enums enable exhaustive switch and IDE support, preventing runtime bugs from string typos.
⚠️Common Wrong Answer
Using String for genre or playback state leads to silent runtime bugs due to typos and no compile-time checks.
🔌
API Design Challenge
Design APIs for adding/removing tracks to playlists and albums, and managing playback queue operations with concurrency safety.
  • Use synchronized methods or concurrent collections to ensure thread-safe modifications.
  • Expose methods like addTrack(Track t), removeTrack(Track t), enqueue(Track t), dequeue() with proper synchronization.
  • Consider using volatile for current playing track reference to ensure visibility across threads.
📊
Scoring Rubric
▼ Show
Must Have - 60%
  • abstract class Track with instance fields for metadata
  • synchronized methods in Playlist for thread-safe modification
  • volatile and synchronized usage in PlaybackQueue for concurrency correctness
  • aggregation relationships between Album, Playlist, and Track with lifecycle explanation
  • use of enums for fixed sets like TrackGenre and PlaybackState
Good to Have - 30%
  • explanation of thread interleaving causing race conditions without synchronization
  • discussion of why interface cannot hold instance state in Java
  • explanation of lifecycle ownership preventing tight coupling
  • use of synchronized keyword on key methods with full Java signatures
  • discussion of SOLID principles enforced by design choices
Bonus - 10%
  • implementation of playback queue with concurrent-safe iterator
  • support for playlist ordering with thread-safe index management
  • explanation of double-checked locking if singleton pattern used for central library manager
Using String for TrackGenre or PlaybackState leads to runtime typo bugs and no compile-time checks, causing silent failures and bugs that are hard to detect.
Using interface for Track with fields causes inability to hold state and code duplication, violating DRY and leading to maintenance issues.
Not synchronizing Playlist modification methods causes race conditions and corrupted playlists, resulting in lost updates and inconsistent state.
Not using volatile for currentTrack causes stale data visibility across threads, leading to playback inconsistencies and bugs.
Using composition for Album->Track ties lifecycle incorrectly, preventing Track reuse and violating single responsibility principle.
✅
Model Answer
▼ Reveal

The design uses an abstract class Track to encapsulate track metadata state, as interfaces in Java cannot hold instance fields. Albums and Playlists aggregate Tracks, reflecting shared lifecycle ownership without tight coupling. Playlist methods addTrack and removeTrack are synchronized to ensure thread-safe modifications. PlaybackQueue uses a volatile Track currentTrack for visibility and synchronized enqueue/dequeue methods for atomic updates. Enums TrackGenre and PlaybackState provide compile-time safety and prevent runtime errors.

  • Use abstract class Track to hold instance state because interfaces cannot hold fields in Java
  • Synchronize Playlist modification methods to prevent race conditions during concurrent access
  • Use volatile for currentTrack in PlaybackQueue to ensure visibility across threads
  • Aggregate Tracks in Album and Playlist to allow shared lifecycle and reuse
  • Use enums for TrackGenre and PlaybackState to enforce type safety and prevent runtime bugs
  • abstract class Track holds instance state (trackId, title, artist, duration) - interface cannot hold fields in Java
  • Album aggregates Tracks using aggregation relationship - Tracks exist independently and can be shared
  • Playlist aggregates Tracks and uses synchronized methods addTrack/removeTrack to ensure thread-safe modifications
  • PlaybackQueue uses volatile Track currentTrack for visibility and synchronized enqueue/dequeue methods for atomic queue updates
  • Enums TrackGenre and PlaybackState provide compile-time safety and exhaustive switch support, preventing runtime errors
  • Lifecycle ownership: Tracks exist independently; Albums and Playlists aggregate Tracks without composition to avoid lifecycle coupling

Practice

(1/5)
1. In designing the Parking Lot system, should Vehicle be an abstract class or an interface, and why?
easy
A. Vehicle should be an abstract class because it holds common state like license plate and size.
B. Vehicle should be an interface to allow multiple inheritance of vehicle types.
C. Vehicle should be a concrete class to allow direct instantiation of generic vehicles.
D. Vehicle should be an interface because it only defines behavior without state.

Solution

  1. Step 1: Identify if Vehicle has shared state

    Vehicle has common fields like license plate, size, and type that need to be stored and reused.
  2. Step 2: Abstract class vs interface for shared state

    Interfaces cannot hold state in Java (prior to default methods), so abstract class is needed to encapsulate shared state and partial implementation.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Shared state in Vehicle -> abstract class needed [check]
Hint: Shared state? Use abstract class, not interface [check]
Common Mistakes:
2. To add a WeekendFeedStrategy that changes the feed ranking on weekends, which minimal change respects SOLID principles in the current design where FeedService uses a FeedStrategy interface?
medium
A. Modify FeedService to add weekend logic inside existing methods.
B. Add a boolean flag isWeekend in FeedService and switch logic internally.
C. Create a new WeekendFeedStrategy implementing FeedStrategy and inject it without changing FeedService.
D. Extend FeedService with a subclass WeekendFeedService overriding methods.

Solution

  1. Step 1: Identify applicable SOLID principle

    Open/Closed Principle (OCP) requires new behavior added via new classes, not modifying existing ones.
  2. Step 2: Why new strategy class fits OCP and DIP

    Creating WeekendFeedStrategy implements FeedStrategy and can be injected, so FeedService remains unchanged and depends on abstractions.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    New strategy class + injection -> respects OCP and DIP [check]
Hint: New strategy class + inject -> OCP and DIP respected [check]
Common Mistakes:
3. In the Tic-Tac-Toe game server, the makeMove() method is not synchronized. Which exact sequence of events between Thread 1 and Thread 2 can cause two players to place their marks on the same cell (double-booking)?
medium
A. Thread 1 and Thread 2 both call makeMove() but on different board instances.
B. Thread 1 reads cell as empty, Thread 2 reads cell as occupied, both proceed to write.
C. Thread 1 locks the board, Thread 2 waits, then both mark different cells simultaneously.
D. Thread 1 checks cell is empty -> true, context switch before marking; Thread 2 checks cell is empty -> true, both mark cell concurrently.

Solution

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

    Without synchronization, the check-then-act sequence is vulnerable: Thread 1 checks if cell is empty and is preempted before marking.
  2. Step 2: Trace Thread 2's actions

    Thread 2 also checks the same cell, sees it empty, and proceeds to mark it. Both threads mark the same cell, causing double-booking.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Check-then-act gap without sync -> double-booking race [check]
Hint: Check empty -> context switch -> both mark same cell [check]
Common Mistakes:
4. To add support for ElectricCar needing ChargingSpot in the current parking lot design, what is the minimum change that respects extensibility and avoids modifying existing classes?
hard
A. Modify Vehicle base class to include charging logic and add ChargingSpot as a subclass of ParkingSpot.
B. Add charging logic inside ParkingLot class and create a new ChargingSpot type without changing enums.
C. Create a new ElectricVehicle interface and modify existing classes to implement it.
D. Add ELECTRIC_CAR to VehicleType enum, CHARGING to SpotType enum, create ElectricCar subclass, and update SpotCompatibility.COMPATIBILITY_MAP accordingly.

Solution

  1. Step 1: Identify extensibility without modifying existing classes

    Enums are extended with new constants; new subclass ElectricCar is created without changing existing classes.
  2. Step 2: Update compatibility map to include new vehicle and spot types

    SpotCompatibility.COMPATIBILITY_MAP is updated to reflect which spots fit ElectricCar, preserving OCP.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Extend enums + new subclass + update compatibility map -> no existing class modified [check]
Hint: Extend enums + new subclass + update map -> no existing class changed [check]
Common Mistakes:
5. You need to add support for ElectricCar and ChargingSpot in an existing parking system using Observer pattern. What is the minimum change to the current design to support this without modifying existing classes?
hard
A. Add a boolean isElectric flag to Vehicle and Spot classes and update existing logic to handle charging spots.
B. Modify Vehicle and Spot classes to include electric-specific fields and logic for charging compatibility.
C. Create a new ElectricCar class and ChargingSpot class, then modify the Subject to notify these new types explicitly.
D. Add ELECTRIC_CAR to VehicleType enum, CHARGING to SpotType enum, create ElectricCar subclass, and update SpotCompatibility map externally.

Solution

  1. Step 1: Identify extensibility requirements

    Adding ElectricCar and ChargingSpot should not modify existing classes to respect OCP.
  2. Step 2: Evaluate options

    Modify Vehicle and Spot classes to include electric-specific fields and logic for charging compatibility. and D modify existing classes, violating OCP; Create a new ElectricCar class and ChargingSpot class, then modify the Subject to notify these new types explicitly. modifies Subject, increasing coupling.
  3. Step 3: Add ELECTRIC_CAR to VehicleType enum, CHARGING to SpotType enum, create ElectricCar subclass, and update SpotCompatibility map externally. adds new enum values and subclasses, updating compatibility externally, preserving existing class integrity.

  4. Final Answer:

    Option D -> Option D
  5. Quick Check:

    New enums and subclasses added externally -> existing classes unchanged [check]
Hint: Add enums and subclasses externally, keep existing classes untouched [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