Object-Oriented Design for a Musical Jukebox
Problem
Design a musical jukebox using object-oriented principles that plays songs from a collection (CDs or playlists), supports queuing tracks, and allows a user to interact with playback via a CD player and a track selector.
Solution
We reuse the database-oriented music library design for persistent storage (artists, albums, tracks). This note focuses on the runtime object model: playlists, playback components, user sessions, and track selection.
1. Requirements Analysis
Functional Requirements:
- Maintain a catalog of songs (via CDs and/or playlists).
- Support creating and managing playlists (queueing tracks, peeking next track).
- Play, pause, and select specific tracks via a CD player or track selector.
- Allow user sessions where a user can control playback (processOneUser).
Data/Metadata Requirements:
- Song metadata: name, artist, year, genre.
- Playlist: ordered collection (queue) of Song objects and current track pointer.
- CD: physical media concept to group tracks (placeholder).
Non-Functional Requirements:
- Clear separation of responsibilities (single responsibility principle) between playback, storage, and UI/session control.
- Extensibility to support additional input sources (files, streaming) and multiple users.
2. Use Case Diagram
Actors: User, System (playback engine).
Use Case Summary: A User selects a source (CD or playlist), queues or selects tracks, controls playback (play/stop/next), and can switch between playlists or CDs. The System handles actual audio playback and maintains current track state.
graph TD subgraph JukeBoxSystem UC_Select(Select Source: CD / Playlist) UC_Queue(Queue Track) UC_Play(Play / Pause / Stop) UC_Next(Skip / Next Track) UC_GetCurrent(Get Current Track) end User --> UC_Select User --> UC_Queue User --> UC_Play User --> UC_Next User --> UC_GetCurrent System --> UC_Play System --> UC_GetCurrent

3. Class Diagram
Below are detailed class descriptions followed by a compact diagram.
Classes (detailed):
- Song: Value object for a single track. Key attributes: songName, artist, year, genre. Minimal behavior: metadata accessors.
- Playlist: Holds a queue of Song objects and an optional current track pointer. Behaviors: getNextTrackToPlay(), queueUpTrack(Song), removeTrack(), peekCurrent().
- CD: Represents a collection of songs on a physical medium. Behaviors: listTracks(), eject()/insert() (placeholders).
- CDPlayer: Responsible for actual playback control. Holds a Playlist and/or CD reference. Behaviors: playTrack(Song), pause(), stop(), setPlaylist(), setCD(). It is the runtime bridge between data (Playlist/CD) and playback engine (System).
- TrackSelector: Small component that maintains currentSong and methods to set/get it. Used by JukeBox to coordinate which song should be played next.
- User: Identifies the active user/session interacting with the jukebox. Holds id and name; may be extended with preferences.
- JukeBox: High-level orchestrator that aggregates CDPlayer, current User, CD collection, and TrackSelector. Exposes processOneUser(User) and getCurrentTrack().
- Facilities / CD collection: Represent auxiliary data like a user's available CDs; kept as collections in JukeBox.
classDiagram class Song { +String songName +String artist +int year +String genre } class Playlist { +Song track +Queue~Song~ queue +getNextTrackToPlay() +queueUpTrack(Song) } class CD { +List~Song~ tracks +listTracks() } class CDPlayer { +Playlist p +CD c +getPlaylist() +setPlaylist() +getCD() +setCD() +playTrack(Song) } class TrackSelector { +Song currentSong +setTrack(Song) +getCurrentSong() } class User { +long ID +String name +getName() +setName() } class JukeBox { +CDPlayer cdPlayer +User user +Set~CD~ cdCollection +TrackSelector ts +getCurrentTrack() +processOneUser(User) } Playlist --> Song CD --> Song JukeBox --> CDPlayer JukeBox --> User JukeBox --> TrackSelector CDPlayer --> Playlist CDPlayer --> CD
4. Activity Diagrams
Activity: Play Next Track
graph TD A[Current track ends or user presses next] --> B[TrackSelector requests next track] B --> C[Playlist.peek()] C --> D[CDPlayer.playTrack(next)] D --> E[Update TrackSelector.currentSong]
Activity: Queue New Track
graph TD U[User selects track to queue] --> Q[Playlist.queueUpTrack(track)] Q --> R[If player idle -> play next] R --> S[Update UI]
5. High-Level Code Implementation
The original Java code is preserved as the primary skeleton. Below are cleaned Java snippets and a Python equivalent with type hints.
Java (cleaned skeleton)
public class Song {
private String songName;
private String artist;
private int year;
private String genre;
// getters/setters
}
public class Playlist {
private Song track;
private java.util.Queue<Song> queue;
public Playlist(Song track, java.util.Queue<Song> queue) { this.track = track; this.queue = queue; }
public Song getNextTrackToPlay() { return queue.peek(); }
public void queueUpTrack(Song s) { queue.add(s); }
}
public class CD { private java.util.List<Song> tracks; }
public class CDPlayer {
private Playlist p;
private CD c;
public Playlist getPlaylist() { return p; }
public void setPlaylist(Playlist p) { this.p = p; }
public CD getCD() { return c; }
public void setCD(CD c) { this.c = c; }
public CDPlayer(Playlist p) { this.p = p; }
public CDPlayer(CD c, Playlist p) { this.c = c; this.p = p; }
public CDPlayer(CD c) { this.c = c; }
public void playTrack(Song s) { /* playback glue */ }
}
public class TrackSelector { private Song currentSong; public TrackSelector(Song s) { currentSong=s; } public void setTrack(Song s) { currentSong = s; } public Song getCurrentSong() { return currentSong; } }
public class User { private String name; private long ID; public String getName() { return name; } public void setName(String name) { this.name = name; } public long getID() { return ID; } public void setID(long iD) { ID = iD; } public User(String name, long iD) { this.name = name; this.ID = iD; } public User getUser() { return this; } public static User addUser(String name, long iD) { return new User(name, iD); } }
public class JukeBox {
private CDPlayer cdPlayer;
private User user;
private java.util.Set<CD> cdCollection;
private TrackSelector ts;
public JukeBox(CDPlayer cdPlayer, User user, java.util.Set<CD> cdCollection, TrackSelector ts) { this.cdPlayer = cdPlayer; this.user = user; this.cdCollection = cdCollection; this.ts = ts; }
public Song getCurrentTrack() { return ts.getCurrentSong(); }
public void processOneUser(User u) { this.user = u; }
}
Python (type-hinted skeleton)
from __future__ import annotations
from dataclasses import dataclass, field
from enum import Enum
from typing import List, Deque, Set
from collections import deque
@dataclass
class Song:
song_name: str
artist: str
year: int
genre: str
class Playlist:
def __init__(self, track: Song | None = None) -> None:
self.track: Song | None = track
self.queue: Deque[Song] = deque()
def get_next_track_to_play(self) -> Song | None:
return self.queue[0] if self.queue else None
def queue_up_track(self, s: Song) -> None:
self.queue.append(s)
class CD:
def __init__(self, tracks: List[Song] | None = None) -> None:
self.tracks: List[Song] = tracks or []
class CDPlayer:
def __init__(self, playlist: Playlist | None = None, cd: CD | None = None) -> None:
self.playlist = playlist
self.cd = cd
def play_track(self, s: Song) -> None:
pass
class TrackSelector:
def __init__(self, current_song: Song | None = None) -> None:
self.current_song = current_song
def set_track(self, s: Song) -> None:
self.current_song = s
def get_current_song(self) -> Song | None:
return self.current_song
class User:
def __init__(self, name: str, id_: int) -> None:
self.name = name
self.id = id_
class JukeBox:
def __init__(self, cd_player: CDPlayer, user: User, cd_collection: Set[CD], ts: TrackSelector) -> None:
self.cd_player = cd_player
self.user = user
self.cd_collection = cd_collection
self.ts = ts
def get_current_track(self) -> Song | None:
return self.ts.get_current_song()
def process_one_user(self, u: User) -> None:
self.user = u

You can download the complete code from github.