Skip to content

HW1: JMU Pizzeria v2

JMU Pizzeria v2

Learning Objectives

  • Interpret UML class diagrams and implement classes based on them.
  • Identify the difference between static and non-static members.
  • Use String methods to validate and process Strings.
  • Develop a small library that can be used to support unit testing.

Overview

Thanks to your help, JMU Pizzeria has become super-successful! Now, they are expanding their menu to include other menu items, such as different sized pizzas, breadsticks, and more.

The problem with the PizzaPricer class you made last time is that everything is fixed, or hard-coded. For example, the price of a box is always $28. What if we also had a small pizza that costs $16? You would need to make another class, called SmallPricerPricer, to handle that (and one for BreadstickPricer, and one for MediumPizzaPricer, etc.). All the classes would be very similar, just with slightly different values!

That's a lot of duplicate code and it would be very difficult to maintain! It also violates a key rule of software development - "Don't Repeat Yourself", or DRY. We want to avoid duplicating code, and instead, write code that can be reused. Let's change the PizzaPricer from a utility class into a regular class named ItemPricer that can be instantiated with different individual prices.

With the old design, you would need multiple utility classes, and use them like so:

double = PizzaPricer.priceFor(10) + SmallPricerPricer.priceFor(12);

With the new design, you would only need one class, and use it like so:

ItemPricer largePricer = new ItemPricer(8, 28.00, 4.00); // 8 slices per box, $28 per box, $4 per slice
ItemPricer smallPricer = new ItemPricer(6, 16.00, 3.00); // 6 slices per box, $16 per box, $3 per slice
double total = largePricer.priceFor(10) + smallPricer.priceFor(12);

Coupon Codes?

The previous design also had a limitation with coupons. There could only be one coupon code and the discount functionality was directly tied into the functionality for other pricing. Yuck. This violated the "Single Responsibility Principle". We want to make our classes (and methods) each have essentially one thing they're responsible for.

JMU Pizzeria also wants to be able to have multiple coupon codes that customers can use. In addition, because their customers kept getting confused, they want the coupon codes to be more lenient - meaning that 0 (zero) can be substituted for O, extra spaces at the start and end of the inputted code don't matter, and it is case-insensitive.

Let's design a separate Coupon class that can be used like so (continuing our example):

...
Coupon welcome = new Coupon("WELCOME-BACK", 10); // Code is "WELCOME-BACK", discount is 10% off
total = welcome.applyCoupon(total, "WELC0Me-BaCk  "); // This should apply the coupon!

The Classes to be Written

You must write a "normal" class named ItemPricer, another class named Coupon, and a utility class named EqualityChecker. The ItemPricer and Coupon classes are specific to restaurants like JMU Pizzeria. The EqualityChecker class can be used to test classes that are written for many different kinds of products.

The ItemPricer Class

A ItemPricer object can be used to price any item that can be sold individually or in boxes.

The UML Class Diagram

The following UML class diagram provides an overview of the attributes and methods in this class (which must be in the hws.hw2 package).

classDiagram
    class ItemPricer {
      -boxSize : int
      -pricePerBox : double
      -pricePerItem : double
      +ItemPricer(boxSize : int, pricePerBox : double, pricePerItem : double)
      +needAnExtraBox(numberOfItems : int) boolean
      +numberOfBoxes(numberOfItems : int) int
      +numberOfFullBoxes(numberOfItems : int) int
      +numberOfExtras(numberOfItems : int) int
      +priceFor(numberOfItems : int) double
    }

Notice that there are several important differences between this "normal" class and the utility class from the previous assignment.

  • All of the attributes are now non-static and, so, belong to each individual instance of the class rather than the class itself.
    • This lets you make different instances of the class for different purposes, like small vs. large pizzas!
  • The attributes are now initialized in an explicit value constructor.
  • The methods are now non-static and, so, make use of the non-static attributes (that have different values for each object in the class).
  • Also note that we no longer have methods related to coupons!
    • The finalPrice() method is no longer needed, so it has been removed.

Detailed Design Specifications

  1. The constructor must initialize all of the object's attributes.
    • Hint: There are three attributes, what are they?
  2. The other methods must conform to the specifications for the PizzaPricer class, but must work for anything that can be sold in boxes or individually (not just pizza 😋).

Note that the official tests for the PizzaPricer class did not necessarily test that your submission satisfied all of the specifications. So, you may think your code was correct when it wasn't. Make sure you read all of the specifications for this assignment.

The Coupon Class

The Coupon class is designed to check if a given code matches a coupon code, and provide a discount if so.

The UML Class Diagram

The following UML class diagram provides an overview of the attributes and methods in this class (which must be in the hws.hw2 package).

classDiagram
    class Coupon {
      -discountCode : String
      -discountPercent : double
      +Coupon(discountCode : String, discountPercent : double)
      +applyCoupon(total : double, discountCode : String) double
      -canApplyCoupon(discountCode : String) boolean
    }

Note that there is now a public applyCoupon method that takes in a total amount and applies a discount if the code is valid.

Also note that the canApplyCoupon method should be private! This means that code inside the Coupon class can (and should) call it, but other classes cannot. We typically write these "private helper methods" to break down complex functionality into smaller parts.

Detailed Design Specifications

  1. The constructor must initialize all of the object's attributes.
    1. The discountCode provided to the constructor is guaranteed to not contain numbers, be all upper-case, and have no leading or trailing spaces.
    2. The discountPercent represents the percentage discount the coupon can apply, e.g. 5.0 represents 5%.
  2. canApplyCoupon() must check if the discountCode provided as a parameter matches the discountCode attribute of the object. However, it now allows for the following:
    1. The number 0 can be substituted for the letter O in the code.
    2. Leading/trailing spaces in the inputted code should be ignored.
    3. The comparison must be case-insensitive.
    4. Hint: What String methods would be helpful? (see String docs; w3schools reference)
  3. applyCoupon() must first check if the coupon can be applied.
    1. If so, it must return the total amount after applying the discount.
      • E.g., if the discount is 10.0, then the total amount should be reduced by 10%.
    2. If not, it must return the original total amount.
    3. Do not duplicate code from the canApplyCoupon() method!

The EqualityChecker Class

The EqualityChecker class is a utility class that can be used to simplify the process of testing other classes. Such a system is sometimes referred to as a test harness or a testing framework. This class must conform to the course style guide (because it is not a test, it is a test harness.)

The UML Class Diagram

The following UML class diagram provides an overview of the attributes and methods in this class (which must be in the hws.hw2 package).

classDiagram
    class EqualityChecker {
      <<Utility>>
      +forEqualDoubles(expected : double, actual : double, tolerance : double, description : String) boolean$
      +forEqualInts(expected : int, actual : int, description : String) boolean$
      +forEqualStrings(expected : String, actual : String, description : String) boolean$
      +forFalse(actual : boolean, description : String) boolean$
      +forTrue(actual : boolean, description : String) boolean$
    }

Detailed Design Specifications

In addition to the specifications contained in the UML class diagram, this class must conform to the following specifications.

  1. forEqualDoubles() must check to determine if the attributes expected and actual differ by more than the tolerance.
    1. If so, it must return false and print the values of description, expected, and actual (in that order) using the format String "%s Expected: %f, Actual: %f\n".
    2. If not, it must return true (and not print anything).
  2. forEqualInts() must check to determine if the attributes expected and actual differ.
    1. If so, it must return false and print the values of description, expected, and actual (in that order) using the format String "%s Expected: %d, Actual: %d\n".
    2. If not, it must return true (and not print anything).
  3. forEqualStrings() must check to determine if the attributes expected and actual differ.
    1. If so, it must return false and print the values of description, expected, and actual (in that order) using the format String "%s Expected: %s, Actual: %s\n".
    2. If not, it must return true (and not print anything).
  4. forFalse() must check to determine if the attribute named actual is false
    1. If so, it must return true (and not print anything).
    2. If not, it must return false and print the description using the format String "%s Expected: false, Actual: true\n".
  5. forTrue() must check to determine if the attribute named actual is true
    1. If so, it must return true (and not print anything).
    2. If not, it must return false and print the description using the format String "%s Expected: true, Actual: false\n".

For example, one might implement the forEqualInts() method as follows:

    /**
     * Display an alert if the actual int value is not equal to the
     * expected value.
     *
     * @param expected     The expected value
     * @param actual       The actual value
     * @param description  A description of the test
     * @return true if the two are equal; false otherwise
     */
    public static boolean forEqualInts(int expected, int actual,
            String description) {
        if (expected != actual) {
            System.out.printf("%s Expected: %d, Actual: %d\n",
                    description, expected, actual);
            return false;
        } else {
            return true;
        }
    }

An Existing Class

A main class (i.e., a class with a main() method) that you can use to test the ItemPricer class has already been written. It is named ItemPricerTest and the source code (i.e., the .java file) is available at:

📄 ItemPricerTest.java

Unlike the PizzaPricerTest class from the previous assignment, this class makes use of the EqualityChecker class that you have to write. So, you will not be able to test your ItemPricer class until you write the EqualityChecker class.

Like the PizzaPricerTest class, this class does not conform to the style guide. As mentioned before, most organizations (including most faculty in the Computer Science Department at JMU) do not require that tests conform to the style guide.

Submission

You must submit (using Gradescope):

  1. Your implementation of the ItemPricer class, the Coupon class, and the EqualityChecker class. Do not include the ItemPricerTest class or other tests you may have written for the other classes.

There is no limit on the number of submissions. Note that your submission will not be graded if it does not comply with the specifications. So, your submission should include a stubbed-out version of all of the classes. (This will allow you to get credit for the classes/methods that you do implement correctly.)

Grading

Your code will first be graded by Gradescope and then by the Professor. The grade you receive from Gradescope is the maximum grade that you can receive on the assignment

Gradescope Grading

Your code must compile (in Gradescope, this will be indicated in the section on "Does your code compile?") and all class names and method signatures must comply with the specifications (in Gradescope, this will be indicated in the section on "Do your class names, method signatures, etc. comply with the specifications?") for you to receive any points on this assignment. Gradescope will then grade your submission as follows:

Criterion Points Details
Conformance to the Style Guide 20 points (Partial Credit Possible)
Correctness 80 points (Partial Credit Possible)

Gradescope will provide you with hints, but may not completely identify the defects in your submission.

Manual Grading

After the due date, the Professor may manually review your code. At this time, points may be deducted for inelegant code, inappropriate variable names, bad comments, etc.

Since nobody will be looking over your shoulder, you can use any process that you would like to use. However, it is strongly recommended that you use the process described here.

Get Started

  1. Read and understand the entire assignment.
  2. Create a folder for this assignment named hw2 (under src/hws).
  3. Download ItemPricerTest.java to a directory outside of your CS159 folder (e.g., the downloads directory for this course).

Understand the Test Cases

  1. Read and understand the test cases in ItemPricerTest.java.
  2. By hand (i.e., using pencil and paper), calculate the expected answer for each of the test cases in ItemPricerTest.java.

Copy and Rename PizzaPricer.java

  1. Copy PizzaPricer.java from the folder named hw1 to the folder named hw2.
  2. In VS code, rename PizzaPricer.java to ItemPricer.java. (Hint: Right-click on the file name and select [Rename…].)

Make ItemPricer.java a "Normal" Class

  1. Delete the initialization of all of the attributes (and save the file).
  2. Understand why this generated several syntax errors.
  3. Remove the final modifier from all of the attributes (and save the file).
  4. Understand why this resolved the syntax errors.
  5. Make all of the attributes non-static (i.e., remove the static modifiers).
  6. Understand why this generated several style errors.
  7. Understand why this also generated several syntax errors in the methods that follow.
  8. Rename the attributes (and save the file). (Hint: Highlight the attribute identifier, right-click on the highlighted identifier, pull down to [Refactor], and then across to [Rename…], type the new identifier, and press [Enter]. Notice that this changes the identifier everywhere in the class.)
  9. Understand why this resolved the style errors.
  10. Remove the static modifier from all of the methods (and save the file).
  11. Understand why this resolved the syntax errors.
  12. Write the explicit value constructor.
  13. For the parts of the code related to coupons, see below.
    • Note: We are no longer using the finalPrice() method, so you can delete it.

Stub-Out the EqualityChecker Class

  1. Create a version of the EqualityChecker class that contains all of the methods (with appropriate signatures), each of which should return false.
  2. Add the "javadoc" comments to the EqualityChecker class and the methods in it.
  3. Check the style of the EqualityChecker class and make any necessary corrections.

Add ItemPricerTest.java to the hw2 folder

  1. Add ItemPricerTest.java to the hw2 folder.
  2. Make sure there are no compile-time errors in ItemPricerTest.java. If there are, you probably need to fix the stubbed-out version of EqualityChecker.java (since there should be no syntax errors in ItemPricerTest.java).

Implement the EqualityChecker Class

  1. Add the forEqualInts() method. (Hint: See the example above.)
  2. Add the forEqualStrings() method. (Hint: Be careful about how you compare String objects.)
  3. Add the forEqualDoubles() method. (Hint: You should probably use the static abs() method in the Math class.)
  4. Add the other methods.

Test and Debug the ItemPricer Class

  1. Run ItemPricerTest.
  2. If there is any output it means that your code failed a test. Starting with the first failure, debug the relevant method in the ItemPricer class using the process we discussed.
  3. Re-run ItemPricerTest and repeat as necessary.

Make a Separate Coupon Class

  1. Move the code related to discount codes to a new class named Coupon in a new file named Coupon.java.
  2. Implement the constructor.
  3. Implement the canApplyCoupon() method.
  4. Implement the applyCoupon() method.

Test the and Debug the Coupon Class

  1. Write your own main method and call methods from Coupon to test the Coupon class.
  2. Try different codes that should (or should not) satisfy the valid coupon criteria.
  3. Don't forget to check if the discount is calculated correctly.

Questions to Think About

You don't have to submit your answers to these questions, but you should try to answer them because they will help you determine whether or not you understand some of the important concepts covered in this assignment.

  1. Notice that the ItemPricerTest class now uses an array for the input values and uses a loop to run all of the test. Why is this better than the approach used in the previous assignment?
  2. How could you change the ItemPricerTest class so that it keeps a count of the number of failed tests (and reports that number)?
  3. What compile-time errors are generated in the ItemPricer class if you make the attributes in it static? Why?
  4. Why does the ItemPricerTest class test smallPricer again after testing largePricer? In other words, what defects might this re-test identify? (Hint: Think about your answer to the previous question.)
  5. Why would it be nice to be able to overload methods in the EqualityChecker class?