Programming Task 4


Time Estimate: 8 hours

Jump to current week
Requirements

Project Structure


You will continue to add functionality to your existing project from the previous task. There is no new repository to clone.



Specification


In this task, you will test and implement the following specs.

  • ratings.Movie - In the ratings package, a class named Movie with:
    • A constructor that takes a String and an ArrayList of Strings:
      • The String represents the title of the Movie
      • The ArrayList contains the names of the cast members of the Movie as Strings
    • A Method named getCast that takes no parameters and returns an ArrayList with the same cast members that were provided to the constructor
      • This method might not return the same exact ArrayList provided to the constructor, but it will contain the same names in the same order. However, the names might not have the same combination of upper/lower-case letters. Any String in the returned ArrayList containing the correct letters should be considered correct regardless of upper/lower-case
      • Example: In the constructor, you provide the ArrayList ["Chris Pratt", "Zoe Saldana", "Dave Bautista"]. All the following are correct outputs ["Chris Pratt", "Zoe Saldana", "Dave Bautista"], ["chris pratt", "zoe saldana", "dave bautista"], and ["CHRIS pratt", "ZoE SalDANA", "dAVE bautistA"]
      • Note that the String class has a method named equalsIgnoreCase that might prove useful when testing this method
  • ratings.Ratable - In the ratings package, a class named Ratable with:
    • This class will use the default constructor that takes no parameters and does nothing (You don't have to write any constructor)
    • Remove all functionality related to title and ratings from the Song class and add to the Ratable class. This includes title and ratings (LinkedListNode of Ratings) instance variables and all the following methods
      • getTitle
      • setTitle
      • addRating
      • getRatings
      • setRatings
      • averageRating
      • removeRatingByReviewer
  • ratings.Song - Update the Song class to inherit from the Ratable class
    • You removed 2 instance variables and 6 methods from this class while writing the Ratable class. However, Song will now inherit all those variables and methods from the Ratable class
    • You should verify that your Song class does inherit this functionality by running the TestClasses1 tests and verifying that they all still pass even though that code is not in the Song class itself.
    • At this point, the Song class should only have instance variables for the artist and id as well as the following methods with all other state and behavior inherited from Ratable:
      • A constructor that takes 3 Strings
      • getArtist
      • setArtist
      • getSongID
      • setSongID
    • Since the Ratable class does not have a constructor, you should call the inherited setTitle in the Song constructor to set the value of the title instance variable. Alternatively, you may add a Ratable constructor that takes the title as a parameter and explicitly call the super constructor in the Song constructor.
  • ratings.Movie - Update the Movie class to inherit from the Ratable class
    • Movie will now inherit from the Ratable class
    • Similar to the Song class, you may need to call setTitle in the constructor to set the value of the title instance variable since it should be private in the Ratable class
  • bayesianAverageRating - in the ratings.Ratable, add a method named bayesianAverageRating that takes 2 ints and returns a double:
    • The first parameter is a number of extra ratings
    • The second parameter is the value of the extra ratings
    • The method returns the average rating of the song/movie if the extra ratings were added. Note that you are not actually adding the ratings to the Song/Movie. You are only using the extra ratings in your computation of the average
    • Example: If a song has ratings of 4 and 5 and this method is called with parameters 2 and 3 (2 extra ratings of value 3), then the bayesian average is (4+5+3+3)/4 == 3.75 instead of the regular average of 4.5
    • If a song/movie has no ratings and this method is called with 0 additional ratings, return 0.0
    • The method should work as expected in the edge cases of 0 extra ratings (eg. Adding 0 extra ratings of value 3 - the output should be the normal average rating), and a song/movie that has no ratings (eg. Adding any number of extra ratings of value x to a song/movie that has not been rated should return x)
    • If the input is invalid, this method will return 0.0. The input is invalid if the value of the extra ratings is anything other than 1, 2, 3, 4, or 5 OR the number of extra ratings is negative
    • This method will be inherited by both Song and Movie, though you should test this method through the Song and Movie classes. It is acceptable, and expected, that you copy your tests and use the same exact test cases for both Song and Movie when testing this method.
    • Why? When comparing songs (Or anything), it's common to have many songs that are only rated by a single reviewer who gave it a 5/5. This song would have a perfect average rating of 5.0 and would be rated higher than a song that has been rated 100 times with an average rating of 4.99. The second song is well-loved by many people, and you would [likely] rather have that song in your playlist than the song that only 1 person likes and no one else is aware of. We adjust this by adding a few fake ratings to each song. In the example of 2 ratings of 3 being added, the first song would be lowered to an average of 3.67 while the second song's rating would still be 4.99. This provides a more useful rating system than directly taking the average of all ratings.
  • ratings.datastructures.Comparator - This class has been provided in the handout code
    • This class takes a generic type parameter
    • This class uses the default constructor
    • This class has a method named compare that takes 2 parameters, both of the type of the generic
      • This method returns false on all inputs and will be overridden by child classes
  • ratings.datastructures.SongTitleComparator - In the ratings.datastructures, a class named SongTitleComparator that will be used to sort Songs by their title in alphabetical order
    • This class will inherit from the Comparator class with the generic type being Song
    • Override the compare method to take two references to Song objects and return a boolean
      • The method returns true if the title of first Song comes before the title of the second Song alphabetically
      • The method returns false otherwise (Including for Songs with the same title)
      • The method won't take upper/lower-case into consideration (eg. "a" and "A" both come before "b" and "B")
      • If one String is a prefix of another, it should come before the other: "aa" comes before "aaa" (This implies that the empty String comes before every other String)
      • Comparisons can be made using the Strings compareToIgnoreCase method. You are not expected to check the characters of the Strings to determine their order
  • ratings.datastructures.SongBayesianRatingComparator - In the ratings.datastructures, a class named SongBayesianRatingComparator that will be used to sort Songs by their bayesian average rating in decreasing order
    • This class will inherit from the Comparator class with the generic type being Song
    • Override the compare method to take two references to Song objects and returns a boolean
      • This method returns true if the first parameter has a higher bayesian average than the second in, and false otherwise (Including for songs with the same bayesian average)
      • Use 2 extra ratings with value 3 for the bayesian averages


Testing Utilities


TestClasses2: Create a class named TestClasses2 in the tests package and write the following testing utility method in this class.

  • compareArrayListsIgnoreCase - Write a method named compareArrayListsIgnoreCase in the tests.TestClasses2 class that:
    • Takes [references to] 2 ArrayList<String> objects as parameters
    • Returns a boolean that is true if the two lists contain all the same Strings in the same order, but ignoring case. The method either returns false, or fails a JUnit assert, if the lists do not contain all the same values in the same order while ignoring case
    • Note: This is where it's recommended that you use compareToIgnoreCase

Note: You still have to create every class and method from the specification, including the 2 comparator classes, before getting feedback on your submission in Autolab since the grader will not be able to compile if those classes/methods do not exist. You don't have to implement them yet, and they can all return a default value



Testing Requirements


In tests.TestClasses2, add tests for the following functionality. Note that you do not have to test all the functionality from the specification.

  • The Movie classes getCast method. You are expected to call your utility method when writing these test cases. Note that there is not setCast method so the only way to test getCast is by calling the constructor to set the case list
  • The Movie and Song classes bayesianAverageRating method. This will be the same method in both classes, but you should test them both for this task. It is ok if you use the same test cases for both classes.


Programming Requirements


Implement all the classes/methods described in the Specification section. As you're writing this code, you should run your tests to see how you are progressing. It's recommended that you write your tests first, submit to Autolab to make sure you have good testing, then use those verified tests to check your code.



Autolab Feedback


The feedback in Autolab will be given in 4 phases. If you don't complete a phase, then feedback for the following phase(s) will not be provided.

  1. Testing your testing utility method
    • Your testing utility method will be checked with a variety of test cases to ensure that it makes all the required checks. This phase will ensure that your utility method is accurate before you start using it in your tests
  2. Running your tests on a correct solution
    • Your tests will be run against a solution that is known to be correct. If your tests do not pass this correct solution, there is an error somewhere in your tests that must be fixed before you can move on with the assignment. If your tests don't get past this check, you should re-read this document and make sure you implemented your tests and code according the specification. You should also make sure that if there are multiple correct outputs to the input in your tests cases that you accept any of the outputs as correct
  3. Checking your tests for feature coverage
    • The next phase is to check if your tests check for a variety of features defined by different inputs. You should write at least one test case for each feature to pass this phase
    • Passing this phase does not necessarily mean that your testing is completely thorough. Satisfying Autolab is the bare minimum testing requirement. Not all possible inputs are checked, and it is sometimes possible to pass this phase with weak testing. If you are struggling to earn credit for code that you believe is correct, you should write more than the required tests
  4. Running my tests on your solution
    • Once Autolab is happy with your tests, it will run my tests against your code to check it for correctness. If your testing is thorough, and your code passes your tests, then you should pass this phase. If you pass your tests, but fail one of mine, it is an indicator that you should write more tests to help expose your bug

Once you complete all 4 phases, you will have completed this Task and Autolab will confirm this with a score of 1.0 for complete.