CISC 3115

Exercise 5: Fixing the Jukebox

Objectives:

Overview

In this exercise, we're going to update the Jukebox application one more time. Instead of ignoring duplicate songs, now we want to count them. This means we both need to be careful about Song equality—taking into account both artist and title—and we have to think carefully about which object(s) should do the counting, and how.

Tracking Unique Songs

As we discussed at the end of class last week, song titles aren't enough to distinguish between different songs—and if we want to count how many times a song is played, we certainly don't want to give credit to the wrong artists! So, first, fix the Song class so that two Song objects are only treated as equal if they have the same title and the same artist. (If you change the equals() method of a class, what else needs to change?)

Counting Songs

Now, how exactly should we keep track of how many times each song is played? It's tempting to add a "countPlays" instance variable to the Song class, and maybe an "incrementCount()" method. But think about object-oriented design... should a song "know" how many times it's been played? That means it would also have to know (or be told) to reset that count periodically. Plus, what if a song were available on multiple jukeboxes? Why should we expect a song to be able to keep track of all the places it's played? Additionally, if you think about how the jukebox application works, each Song object really represents a single "play" of the song; if I have multiple objects representing multiple plays of the same song, which one should do the counting? So this idea, of having song objects count how many times they're played, doesn't sound like great design—number-of-plays is not really an "essential quality" of a song.

Instead, I think it's more a property of the device that plays songs. So, let's make it the responsibility of the jukebox to count the songs it plays—resetting the count will be much easier than (say) going through all the songs and resetting their individual counts.

That requires some kind of way to both keep track a list of all the songs that have been played so far and associate each of those songs with the number of times it's been played. Here's a bad idea: keep an array (or ArrayList) of Song objects, and then set up another array (or ArrayList) of int to hold the count of each cong, so that the elements in each array "match" each other. It's going to get time-consuming to search that array for a song—or, if we keep that array sorted, then we're going to have super-duper careful not to mess up the order of the count array.

Here's a better idea. The key in that last paragraph is associate: we want to associate each Song with its count. Often, the word "associate" is a hint that we want to use a Map structure. And that's exactly what we need to do. We can create a Jukebox instance variable, maybe named songCount, that will map a Song object to its count. Specifically, we need to modify the book's Jukebox3class. We'll declare a HashMap instance variable:

HashMap<Song, Integer> songCount = new HashMap<Song,Integer>();
Now, write a method countSongs() that iterates through songList and uses songCount to map every song that’s been played to the count of how many times it’s been played. And add code to the main() method that uses songCount to display the number of times each song has been played.

Here is the book's original code: Jukebox3.java  Song.java

Of course, you'll have to generate your own input text file in order to test your code. Remember that this version of the Song class reads in 4 "tokens" per line, so be sure your input file format matches what the code expects.

If you have time, write code that allows us to compare songs by both title and artist (that is, so that songs with the same title can still be put in order by the artist name.)