Problem statement
Design the object model and core APIs for a music player and its playlist engine that a single app process runs on one device. The engine owns the notion of what is playing, what plays next, and how the running order is built; the surface on top of it (a screen, a set of transport buttons, or a test harness) merely issues commands like play, pause, next, and previous and reads the now-playing state back.
Operating context. One listener on one device at a time, driving a single active playback session. A song is an immutable catalog item (id, title, artist, duration); a playlist is an ordered collection of songs the listener assembles and reorders. When playback starts, the engine builds a play queue from a chosen playlist and a running-order mode, then walks it. Shuffle changes the order the queue is traversed; repeat controls what happens at the end (stop, restart the whole queue, or loop the current song). The listener can also enqueue a song to play next without disturbing the rest of the order. There is no audio decoding, no network, and no concurrent access — one caller on one thread advances the session.
Out of scope. The actual audio codec / output pipeline, streaming and buffering, downloading or caching of media files, cross-device sync of the session, and any UI rendering or gesture handling. Design only the in-process playback and playlist object model, not the media stack or the screen around it.
What to produce. The class hierarchy (entities such as Song, Playlist, PlayQueue, and the Player engine, plus value objects like a repeat or shuffle mode), the public API each class exposes, and the state transitions for the session (e.g. stopped, playing, paused) and for track position. Be explicit about: how shuffle and repeat are modelled as swappable strategies rather than tangled if-branches inside next(); how the play queue is derived from a playlist so that reordering the playlist does not corrupt an in-flight session; how play / pause / next / previous and play-next enqueue mutate the now-playing state; and how a new running-order mode (say, smart shuffle that avoids repeating an artist) is added without editing the transport controls.
Functional requirements
- Start playback from a chosen playlist: build a play queue in the current running-order mode and begin the first track, exposing it as now-playing.
- Handle the transport commands play, pause, next, and previous, updating the now-playing track and the playback status accordingly.
- Apply the shuffle setting so the queue is traversed in a shuffled order, and toggling it back restores a well-defined linear order.
- Apply the repeat mode at the end of the queue: stop, restart the whole queue, or replay the current track, per the selected mode.
- Enqueue a song to play next so it becomes the following track without reordering the songs already queued after it.
- Expose read-only queries for the now-playing track, the playback status, and the upcoming order of the queue.
Non-functional requirements
- Advancing to the next or previous track is O(1), not a rescan of the whole playlist on every transport press.
- Shuffle and repeat are injected strategies, so a new running-order or end-of-queue rule is added without editing next() / previous().
- A Song is immutable once created, so it can be shared across many playlists and queues without defensive copying.
- Reordering or editing the source playlist mid-session must not corrupt or crash the in-flight play queue.
- The engine is unit-testable with no real audio output, no wall clock, and no I/O — commands in, now-playing state out.
- Now-playing and queue reads are immutable snapshots, so a caller cannot mutate the queue and bypass the transport logic.
Topics
- System Design LLD
- Oop Solid
- Patterns Strategy
- Patterns State
- Extensibility