Skip to main content

Lights, Cameras, Objects!

Goals of Assignment


Design

In this homework, you will be writing the functionality for your favorite local movie theater. All movie theaters have Movies, Customers, and Reviews for their movies, so we’ll be making objects for each of these things.


Testing

Motivation

While we’ve been using println statements in main in previous homeworks, for this homework we’re going to use JUnit, which is a industry-grade framework used by companies like Amazon and Google to test their Java code. JUnit is valuable because it lets us maintain enormous test suites that can run automatically and let us know if changes to our code breaks any of our functionality - this becomes super crucial as the projects you’ll be making in your CIS career will grow in size and complexity.

JUnit Reminders

As a review, JUnit works by comparing expected output to actual output on your functions and object state. Here’s an example:

public class ExampleTest {

    @Test
    public void testSumFunction() {
        assertEquals("5 + 6 should be 11", 11, Example.sum(5, 6));
    }

}

We can also test to make sure an exception is thrown properly. Using the same setup as above:

   @Test(expected = IllegalArgumentException.class)
    public void testException() {
        SomeClass.readNonnullFile(null);
    }

You’ll find the following functions from the JUnit library useful:

assertTrue(someConditon);
assertFalse(someCondition);
assertEquals(expectedValue, actualValue);
assertEquals(expectedDouble, actualDouble, deltaValue);
assertArrayEquals(expectedArray, actualArray);

Running Junit on Codio

In order to run your tests, go to Tools -> JUnit and click the blue “Execute All” button. After you’ve run them once, you can also click the blue “Re-execute” button. Each test will either say passed or failed - passed means your test worked, and failed means it does not. You can click on a failed test to see more information about the failure.


Disclaimer

There are many different methods to write in this assignment, but each should be solved with a no more than a few lines of code, with a few exceptions. If you find yourself stuck trying to write very long methods, that’s a good sign that you should take a break and get some clarification on Piazza or in Office Hours.


Part 1: Review.java

We’re going to take a bottom-up approach to making this movie theater. First, we’re going to implement the most basic object, which is the Review object. A Review has three properties: a Movie object, a score, and a description. Score is an int that must be in the range of [0, 100] inclusive - if it is not, then you should throw an IllegalArgumentException with an appropriate error message. This class is what we refer to as a POJO, or plain old Java object - it just holds variables and can return their values.

Implement the constructor to store the given values in these fields. Note that there is no isPositive parameter - you should say that isPositive is true if the score given is at least 70. Next, implement each of the four getters: getMovie(), isPositive(), getScore(), and getDescription().

Here’s an example of how you would instantiate a Review object.

Review review = new Review(someMovieObject, 80, "Really amazing movie!");

Quick check-in: How does this method fit into our concept of ‘Immutability’? Are there any fields that are not fully immutable?


Part 2: Movie.java

In Movie.java, you will be implementing the following methods:

Note: We have implemented two functions for you that you do not need to change or test: equals() and toString(). These are methods that every object either implicitly or explicitly implements.

Let’s break down each part.

Constructor

For the constructor, you should be storing the parameters to the instance fields, which are String name, String genre, String rating, int tickets, Review[] reviews, and int reviewIndex. You should initialize the length of reviews to be maxReviews. These fields respectively represent the name of the movie, what genre the movie is, the rating of the movie (like G, PG, R), the number of tickets left for the movie before it’s sold out, an array of reviews that customers have left, and an index variable to help you track where you are in the reviews array.

You will also have to make sure that the movie has a valid genre - if the provided genre isn’t in the static array of valid genres, you will throw an IllegalArgumentException with an appropriate error message.

There are some instance fields that are not provided in the constructor - you should initialize these using your best judgment.

Here’s an example of how you would instantiate a Movie object.

Movie movie = new Movie("Memento", "Thriller", "R", 100, 50);

hasTicketsLeft()

Customers can’t see a movie if it’s sold out. Return true if there are still tickets for the movie and false otherwise.

seen()

Every time a Customer sees this movie, they will call this function. You should decrement the tickets. However, if there are no tickets left, you should throw an IllegalStateException.

Quick check-in: How does this method fit into our concept of ‘State’? How about ‘Encapsulation’?

leaveReview(Review review)

Each Movie object should track the reviews that customers leave for it. if the input is null, throw an IllegalArgumentException - otherwise, add the review to your array reviews. The instance field reviewIndex will be really useful to you here - you should use that to track where you should put the next review in the array. Note that if there is no space left in your reviews array, you should just return without doing anything.

rottenTomatoes()

Customers don’t want to have to read every review for a movie before deciding if they want to see it. Instead, they should just be able to see the percentage of people that liked it when they saw it! To this end, we will be implementing the Rotten Tomatoes approach - we return the number of positive reviews times 100 divided by the number of total reviews. If there are no reviews yet, just return 50.

Example: Say that there are five reviews for our movie so far, with scores of 25, 50, 60, 80, and 90. In this case, we have 2 positive reviews and 3 negative reviews. In this case, we will return 40, because 2/5 reviews are positive and we want to return the percent of positive reviews.

Getters

You should implement getters for the three private variables name, genre, ticketsLeft, and rating. We have started these for you.


Part 3: ReviewTest.java

This is going to be our simplest class to test, because it contains fields but has no methods that change its state or interact with other objects. Complete the tests we have started for you in ReviewTest.java. You need to implement the assert statements in each test.


Part 4: MovieTest.java

For this file, you should test the following: Make sure your constructor properly initializes your fields. Use your getter methods to help you test this. You should follow the same logic and structure that you used for your ReviewTest.java file.

Reminder: If you are using junit to test equality with doubles, you will need to add an additional parameter for a delta which represents how far apart the two values can be for the test to pass. For example, if you are expecting 100, you could set the delta to be 0.01 and say assertEquals(100, function(x, y), 0.01).


Part 5: Customer.java

We’re now ready to implement our Customer object. A Customer has a name and a minimum Rotten Tomatoes score that a movie has to achieve before they’ll be willing to see it, which we call preferredMinScore. A Customer can also track what movies they’ve seen using moviesSeen and what reviews they’ve left using reviews. You will be implementing the following methods:

Constructor

Initialize your instance fields.

Here’s an example of how you would instantiate a Customer object.

Customer harry = new Customer("Harry Smith", 85);

goodEnough(Movie movie)

A movie is good enough for a customer to see if the movie’s rotten tomatoes score is at least their preferredMinScore. First, check if the movie is null - if it is, then throw an IllegalArgumentException. If not, then if the customer decides to see the movie, this method should return true, and if the customer doesn’t decide to see the movie, this method should return false.

seeMovie(Movie movie)

If the input movie is null, throw an IllegalArgumentException. Otherwise, do the following:

leaveReview(Movie movie, int score, String description)

Getters

Now implement the four getters we have outlined for you.


Part 6. CustomerTest.java

Follow the guidelines from part 4 to write CustomerTest.java. Again, be sure to test the constructor and all methods on both typical inputs and edge cases.


Part 7. BoxOffice.java

We have now finished implementing all of our lower-level objects and are ready to tackle the BoxOffice class. Running a movie theater is all about marketing, so our BoxOffice object is going to be in charge of tracking the best movies based on their reviews. First, let’s implement our constructor.

Constructor

The constructor for a BoxOffice object should save the theater name and initialize the bestMovie and secondBestMovie fields to be null for now. We also want to track the total number of movie theaters that are created, so update the static totalTheaters field as well.

marketing(Movie[] allMovies)

This is where we will do the work to figure out what movies we’re going to be advertising. Note that this is a private method. From the perspective of a user of the class, they will be calling either bestNewRelease() or secondBestNewRelease(). Both of those functions will use this function, but no user will ever call this function by itself. Because of that, we are going to hide it to keep things simpler for the user. This is an aspect of abstraction as well as encapsulation.

bestNewRelease(Movie[] allMovies) and secondBestNewRelease(Movie[] allMovies)

First, make sure that allMovies isn’t null and has at least 1 movie. If it doesn’t, throw an IllegalArgumentException. Reviews for movies are always updating, and we don’t want to get out of date on what movies we should be advertising. When someone calls bestNewRelease or secondBestNewRelease, you should re-do your marketing calculations on the provided movies. Then, you can return the best or second best movie, respectively.


Part 8. BoxOfficeTest.java

Follow the same guidelines from part 4 to write this file. Again, you will be testing the constructor and all methods, and be sure to use both normal and edge cases.


Submission

Please upload all of your .java files and your readme to Gradescope for submission.