Two diamonds on one diagram, one filled and one hollow. Write the playlist so the cover is created and dies with it, and the songs are handed in and outlive it.
A music app has songs, and playlists that group them. A song can sit in many playlists and survives when one is deleted. A playlist's cover picture, though, belongs to that one playlist and goes with it. The diagram draws the two differently:
%% title: Playlist
classDiagram
class Song {
+title str
+seconds int
}
class Cover {
+label str
+closed bool
+close()
}
class Playlist {
+name str
+add(song)
+total_seconds() int
+delete()
}
Playlist "1" *-- "1" Cover : cover
Playlist "*" o-- "*" Song : songsThe filled diamond (*--) is composition: the playlist creates its cover, nobody else holds it, it ends when the playlist ends. The hollow diamond (o--) is aggregation: the playlist only collects songs that were created elsewhere and handed to it.
Build this API (names are Python's; JavaScript and TypeScript use camelCase where they differ: totalSeconds):
Song(title, seconds) with .title and .seconds.Cover(label) with .label and .closed (starts False); close() sets closed to True.Playlist(name) with .name, .songs (starts empty) and .cover. The playlist creates its own Cover in its constructor: the label is the first letter of the name in capitals ("road trip" gives "R"). It is never passed in.Playlist.add(song) keeps the very Song object it is given (never a copy, never a new Song). Adding a song that is already in this playlist raises ValueError (JavaScript: Error whose message contains already).Playlist.total_seconds() is the sum of its songs' seconds.Playlist.delete() closes its cover and empties .songs for that playlist. It must not touch the songs themselves: another playlist that holds the same song still has it.The tests check that the cover is created by the playlist (each playlist has its own), that songs are the same objects you passed in and can be shared by two playlists, and that deleting a playlist ends the cover but not the songs.
Write your code, then run 9 example tests and 3 hidden tests. Each run is limited to 8 seconds: an infinite loop gets stopped and the runtime restarts, your code stays.