PA1: Teethbrushes

Programming Assignment 1

Teethbrushes

Learning Objectives

This programming assignment is designed to help you learn several things. First, it will help you learn about specialization (i.e., derived classes), inheritance, and overriden methods. Second, it will help you learn about reading from and writing to the file system (i.e., file input and output). Finally, it will help you become familiar with packages.

Overview

You may have heard, or you may have observed first-hand, that computer science students sometimes have personal hygiene problems. (Not you of course, but perhaps some of your fellow students?) A recent survey identified part of the problem. Many computer science students only brush one tooth (rather than all of them) because they were told to use a toothbrush and the word tooth is singular. An enterprising company has decided to take advantage of this confusion by selling teethbrushes instead.

At the moment, they are starting to build relationships. For example, they have contracted with “Dr. Teeth and the Electric Mayhem” to write their advertising jingles, and they have contracted with you to implement some of the classes needed for their inventory system.

Background

A physical Teethbrush consists of a head (and associated handle or stem) while a physical ElectricTeethbrush consists of a head and a body. The bristles in the head can be flat or polished (i.e., rounded). The body of an ElectricTeethbrush can be rechargeable or not, and can provide ultrasonic cleaning (via the head) or not.

The cost of a Teethbrush is the sum of the BASE_HEAD_COST ($2.00), the hardness cost, and the POLISHED_UPCHARGE ($0.75) if the bristles are polished. The hardness cost is the product of the hardness (which is in the closed interval [0, 5]) of the bristles and the HARDNESS_UPCHARGE ($0.10). So, the harder the bristles, the higher the cost. For example, a Teethbrush that has polished bristles with a hardness of 5 costs $2.00 + $0.75 + $0.50 = $3.25

The head of an ElectricTeethbrush costs the same as the head of a Teethbrush. However, an ElectricTeethbrush comes with multiple heads, whereas a Teethbrush has only one. Hence, the cost of an ElectricTeethbrush is the product of the NUMBER_OF_HEADS (5) and the cost of a Teethbrush, plus the BASE_BODY_COST ($5.00), plus the RECHARGEABLE_UPCHARGE ($20.00) and the ULTRASONIC_UPCHARGE ($10.00) as appropriate. For example, a basic ElectricTeethbrush that has polished bristles with a hardness of 0 has an initial cost of $13.75 + $5.00 = $18.75, and a rechargeable version would cost $18.75 + $20.00 = $38.75.

The shipping cost of an order is the product of the unit shipping cost and the order size, plus a STOCKING_COST ($25.00) if the size of the order is greater than the current inventory level. The unit shipping cost is either the UNIT_SHIPPING_COST_OVERSEAS ($0.30) or the UNIT_SHIPPING_COST_LOCAL ($0.10), reduced by the LARGE_ORDER_DISCOUNT (30.0%) if the order is greater than LARGE_ORDER (1000). For example, suppose someone wants to ship an order of 2000 Teethbrush overseas when there are 100 in inventory. The unit shipping cost will be $0.21 (the $0.30 minus the large order discount of $0.09), meaning the total shipping cost will be $445.00 (i.e., $0.21 x 2000 = $420.00 plus the $25.00 stocking cost).

The Classes to be Written

You must write the Teethbrush, ElectricTeethbrush, and ProductFileProcessor classes that are summarized in the following UML class diagram.

Class Diagram

Note that attributes and methods that are inherited are not made explicit in this diagram. If a method appears in both a base class and a derived class it means that the method in the derived class overrides the method in the base class.

The Teethbrush Class

As discussed above and as shown in the UML class diagram, a Teethbrush object is characterized by the hardness of the bristles (on a scale of 0-5), and whether the bristles are polished or not (i.e., whether the part of the bristle that comes in contacts with the teeth is flat or rounded). A Teethbrush object also has an associated inventory level.

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

  1. Any time someone attempts to set the hardness attribute to a value less than 0 it must be set to 0.
  2. Any time someone attempts to set the hardness attribute to a value greater than 5 it must be set to 5.
  3. The changeInventory() method can be used to increase (with a positive parameter) or decrease (with a negative parameter) the inventory attribute. It must not change the inventory attribute (and must return false) if the change would make the inventory attribute negative. Otherwise, it must make the change and return true.

The ElectricTeethbrush Class

As discussed above and as shown in the UML class diagram, in addition to the attributes of a Teethbrush, an ElectricTeethbrush can be rechargeable or not, and can be ultrasonic or not.

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

  1. The cost() method in the ElectricTeethbrush class must not duplicate any code in the method that it overrides.

The ProductFileProcessor Class

The ProductFileProcessor is a utility class that can be used to read/write Teethbrush and ElectricTeethbrush information from/to the file system in (a simplified) comma-separated-valued (CSV) format in which fields are delimited by a comma and there is a newline character at the end of a record. For example, the CSV file for an electric toothbrush might be as follows:

0,3,true,false,true

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

  1. The file name to use when reading and writing must be the model name followed by ".bru". So, for example, the information about model "E1003" must be in a file named "E1003.bru" (in the working directory/folder).
  2. All objects used for reading and writing must be closed when they are no longer needed.
  3. The writeTeethbrush() method must write the inventory, hardness, and polished attributes using "%d,%d,%b\n" as the format String
  4. The writeElectricTeethbrush() method must write the inventory, hardness, polished, rechargeable and ultrasonic attributes using "%d,%d,%b,%b,%b\n" as the format String
  5. The readTeethbrush() method must read a file written by the writeTeethBrush() method, construct and initialize a Teethbrush object, and return it.
  6. The readElectricTeethbrush() method must read a file written by the writeElectricTeethBrush() method, construct and initialize an ElectricTeethbrush object, and return it.

Unit Testing

You must write JUnit tests for the Teethbrush, ElectricTeethbrush, and ProductFileProcessor classes. Your JUnit test suite must cover all statements and all branches (as measured by EclEmma) in all of the classes you write. Your tests must be in a package named testing and each test class must include the word “Test” in its name.

Submission

You must submit (using Gradescope) a .zip file named pa1.zip that contains:

  1. Your implementation of the Teethbrush, ElectricTeethbrush, and ProductFileProcessor classes.

  2. Your JUnit tests.

packaged appropriately.

Do not submit any .bru files. To test the read methods in your ProductFileProcessor class you must create a JUnit test that first executes the appropriate write method and then executes the appropriate read method. (This is sometimes called “round trip” testing of input/output.) Note that, to ensure that the write happens before the read, the two must be in the same JUnit test. You can (and should) have multiple tests of this kind, but each must be independent of the others.

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

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:

CriterionPointsDetails
Conformance to the Style Guide0Success Required
Passing Your Tests10All or Nothing; Success Required
Coverage of Your Tests20Partial Credit Possible
Correctness70Partial Credit Possible

Note that some of the criteria are described as “Success Required”. This means that Gradescope will not assess subsequent criteria unless this criterion is satisfied (and you will receive a grade of 0 for the criteria that aren’t assessed). So, for this assignment, if your code does not conform to the style guide then nothing else will be assessed (and you will receive a grade of 0 for the assignment). Similarly, if your code does not pass your tests, nothing else will be assessed and you will receive a grade of at most 10.

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. In Eclipse, create a project for this assignment named pa1. (Remember, do not create a module, and create separate folders for source and class files).

  3. In Eclipse, create three packages (in the project) named product, product.io, and testing. Do not have Eclipse create package-info.java for any of these packages.

  4. Activate Checkstyle for this assignment.

Create Test Cases for Teethbrush and ElectricTeethbrush

  1. By hand (i.e., using pencil and paper), create several black-box tests cases (i.e., inputs and expected outputs) for the cost() method in the Teethbrush class. Some of these tests should involve random inputs and some should involve rules-of-thumb.

  2. By hand, create tests several black-box cases for the shippingCost() method in the Teethbrush class. Some of these tests should involve random inputs and some should involve rules-of-thumb.

  3. By hand, create sevearl black-box tests cases for the cost() method in the ElectricTeethbrush class. Some of these tests should involve random inputs and some should involve rules-of-thumb.

Writing these black-box test cases before you start writing the code is called test driven development (TDD). Using TDD will help you understand the algorithms that you need to write.

Stub-Out the Classes

  1. Stub-out the Teethbrush class.
  2. Stub-out the ElectricTeethbrush class.
  3. Stub-out the ProductFileProcessor class.
  4. Add the “javadoc” comments to the three classes class.
  5. Check the style of the three classes and make any necessary corrections.

Implement and Test the Classes

  1. Implement the constructor, setters, and simple getters in the Teethbrush class.

  2. Test the constructor, setters, and simple getters in the Teethbrush class using the black-box tests (and debug if necessary).

  3. If necessary, add white-box tests to get 100% statement coverage and 100% branch coverage (and debug if necessary).

  4. Implement the constructor and simple getters in the ElectricTeethbrush class.

  5. Test the constructor and simple getters in the ElectricTeethbrush class using the black-box tests (and debug if necessary).

  6. If necessary, add white-box tests to get 100% statement coverage and 100% branch coverage (and debug if necessary).

  7. Implement the shippingCost() method in the Teethbrush class.

  8. Test the shippingCost() method in the Teethbrush class using the black-box tests (and debug if necessary).

  9. If necessary, add white-box tests to get 100% statement coverage and 100% branch coverage (and debug if necessary).

  10. Implement the cost() method in the Teethbrush class.

  11. Test the cost() method in the Teethbrush class using the black-box tests (and debug if necessary).

  12. If necessary, add white-box tests to get 100% statement coverage and 100% branch coverage (and debug if necessary).

  13. Implement the cost() method in the ElectricTeethbrush class.

  14. Test the cost() method in the ElectricTeethbrush class using the black-box tests (and debug if necessary).

  15. If necessary, add white-box tests to get 100% statement coverage and 100% branch coverage (and debug if necessary).

  16. Use the same kind of process to develop, test, and debug the ProductFileProcessor class.

Help

Help is available on the following topics.

Help Creating Packages

Help with creating packages in Eclipse is available on the Department’s Wiki at:

Help Creating Files (Including Files in a Package)

Help with creating files in Eclipse (including files in a package) is available on the Department’s Wiki at:

Help with JUnit

You should be fairly proficient with JUnit at this point (especially since we will be using JUnit for the rest of the semester). However, if you are still having trouble, help is available at:

Help with Coverage

In order to get 100% coverage, you sometimes have to be aware of some quirks of the language you are working in, and the tool set you are using. For more information, see:

Help Creating the .zip File

The directories/folders containing the packages must appear at the top of the .zip file. So, you must not compress the directory/folder containing these directories/folders, you must compress the directories/folders containing the packages. In other words, if you have a directory named src that contains the directories product and testing, you must not compress src, you must compress product and testing. (Note that the io directory/folder will be inside of the product directory/folder.)

Help with creating .zip files is available on the CS Wiki. In particular, see:

Note that your .zip file must only contain .java files, not .class files. This will be much easier to accomplish if, when you create your project, you instruct Eclipse to use separate directories/folders for source and class files.

Help Using a Scanner

There are two delimiters used in CSV files, the comma that separates fields and the newline character that separates records (or lines). To instruct a Scanner to use a particular delimiter or delimiters you must invoke its useDelimiter() method. To instruct it to use just commas as delimiters you must pass it ",", to instruct it to use just newlines as delimiters you must pass it "\n", and to instruct it to use both you must pass it "[,\n]" (which is a regular expression that matches both).

Help Using a BufferedReader

There are two delimiters used in CSV files, the comma that separates fields and the newline character that separates records (or lines). If you choose to use a BufferedReader you will read an entire record (including the newline character) using the readLine() method. You can then tokenize it either using the split() method in the String class (passing it "," as the delimiter) or using a StringTokenizer object (again using "," as the delimiter).

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. Would the design of the Teethbrush class be improved by using a 0-1 indicator for the polished attribute (as discussed in Chapter 6 of Patterns for Beginning Programmers ) rather than using a boolean value? What would change in the constructor, cost() method, and isPolished() method?

  2. What is gained by using specialization in this assignment? In other words, what is gained from having the ElectricTeethbrush class extend the Teethbrush class?

  3. Is there code duplication in the “read” methods of the ProductFileProcessor class? Why is this troubling?

  4. Is there code duplication in the “write” methods of the ProductFileProcessor class? Why is this troubling?

  5. Given the number of classes in this assignment, are the packages that helpful? When is the use of packages likely to be more helpful?