PA2: Big Box Bargains
Categories:
13 minute read
Programming Assignment 2
🛑 To Know Before You Start
Though the classes you must write are not terribly large, and the input/output is similar to what was required to complete the previous assignment, this assignment will be fairly time consuming. This is both because it deals with the most difficult concepts that we have considered this semester, and because you will need to spend a lot of time writing tests.Learning Objectives
This programming assignment is designed to help you learn several things. First, it will help you learn about interfaces and abstract classes, and improve your understanding of specialization. Second it will help you learn about polymorphism and dynamic binding. Third, it will help you learn about static binding. Finally, it will help reinforce your understanding of I/O, packages, and unit testing.
Overview
Big Box Bargains (BBB), a new “big box” store that hopes to compete with stores like Costco and Sam’s Club, has contracted with you to write some of the software they need for their checkout system.
The Components to be Written
You must write the Account
and VIPAccount
classes that encapsulate
membership information that is specific to BBB, as well as some
general purpose components for performing input/output operations on
comma-separated-value (CSV) files. The relationship between these components
is illustrated in the following UML class diagram.
Note that methods in a class that are “concrete” implementations of abstract methods
(that are listed either in an interface or an abstract class) are not explicitly listed in
the class (e.g., the toCSV()
method in the Account
class). Method in a derived class
that override a method in a base class are explicitly listed in the derived
class (e.g., the toCSV()
method in the VIPAccount
class).
🛑 Italics
On some displays, it may be difficult to distinguish the italic font from the upright font in this class diagram. So that there is no confusion, the following are italicized: thefromCSV()
and toCSV()
methods in the CSVRepresentable
interface,
the name of the FileProcessor
class, and the decrypt()
and encrypt()
methods in the FileProcessor
class.The CSVRepresentable
Interface
The CSVRepresentable
interface describes the capabilities of objects
that can be represented in simplified CSV format (i.e., all of the fields
are delimited by commas, the fields are assumed to not contain commas,
and there are no column headers). The toCSV()
method says that
such an object must be able to create a String
representation of
its attributes in simplified CSV format. The fromCSV()
method, on the other hand, says that such an
object must be able to initialize its attributes from a String
representation in simplified CSV format.
Obviously, the fromCSV()
method must tokenize the String
that it
is passed (that has a comma as the delimiter between tokens). In
principle, this can be done in many ways, but you must choose between
two. Specifically, you must use either a Scanner
or a StringTokenizer
(both of which are in the java.util
package) for this purpose. The
fromCSV()
method returns the object used for tokenizing the String
so that it may be used to tokenize any remaining tokens in the
String
. In other words, the String
representation may contain more
tokens than the fromCSV()
method uses, so it must return the object used
for tokenzing in case these tokens need to be used elsewhere.
The FileIdentifier
Class
The FileIdentifier
class encapsulates the “classic” two-part approach
to identifying files. In this “classic” approach, a file identifier
consists of a name (the portion to the left of the dot) and an
extension or type (the portion to the right of the dot). So, for
example, one might identify the file named Student.java
using a
FileIdentifier
that has a name
attribute of "Student"
and an
extension
attribute of "java"
.
This class implements/realizes the CSVRepresentable
interface because
it is common to include FileIdentifier
objects in CSV files.
The toString()
method must return the name
and extension
formatted using the
format String
"%s.%s"
.
The FileProcessor
Class
The FileProcessor
class is an abstract encapsulation of an object
that can read from and write to the file system in a line-oriented
way. The first line of any file it reads or writes must contain the
number of lines in the file.
This class supports encryption and decryption, but those capabilities must be provided by derived classes. In addition to the specifications contained in the UML class diagram, this class must conform to the following specifications.
The
readLines()
method must first read the number of lines in the file. It must then read the subsequent lines, decrypt them, and return aString[]
array that contains the decrypted lines.The
writeLines()
method must first write the length of the parameter namedlines
(on a line of its own). It must then encrypt the elements and write them to the file (one element per line).
The CSVFileProcessor
Class
The CSVFileProcessor
class is a concrete specialization of the
FileProcessor
class. In addition to the specifications contained in
the UML class diagram, this class must conform to the following
specifications.
The
decrypt()
andencrypt()
methods must return the parameters they are passed unchanged. In other words, CSV files must not be encrypted.The
write()
method must create a CSV representation of the objects it is passed and write all of them to the appropriate file (that is passed to the constructor), one per line.The
read()
method must read all of the records from the appropriate file (that is passed to the constructor) and initialize the attributes of the objects that it is passed. It must assume that it is passed an array of objects that has the same number of elements as there are records (one per line) in the file. (Note: This method does not return the elements it reads. Instead, it sets the attributes of eachCSVRepresentable
object it is passed, using thatCSVReprentable
object’sfromCSV()
method.)The
write()
andread()
methods must not duplicate any code in theFileProcessor
class or in the class of theCSVRepresentable
objects that it is passed.
The Account
Class
The Account
class is an encapsulation of a membership account at BBB.
In addition to the specifications contained in the UML class diagram,
this class must conform to the following specifications.
The
purchases
attribute must be used to keep track of the total purchases made by the member (measured in dollars), and thecreditsUsed
attribute must be used to keep track of the total credits used by the member (measured in dollars).The default constructor must initialize both attributes to 0.00.
The
availableCredit()
method must return the available credit, which is based on a percentage (obtained fromgetRewardPercentage()
) of thepurchases
attribute minus the credits used to date. So, for example, ifpurchases
is 2000.00,getRewardsPercentage()
returns 0.01, andcreditsUsed
is 5.00, then this method must return 15.00. Note that the value returned must never be less than 0.00. So, for example, ifpurchases
is 2000.00,getRewardsPercentage()
returns 0.01, andcreditsUsed
is 25.00, then this method must return 0.00.An
Account
holder can use the express line when the total purchases made by the member is greater than 1000.00.The
getRewardPercentage()
method must return 0.01 (i.e., 1 percent) forAccount
holders.The
increaseCreditsUsed()
method must increase thecreditsUsed
attribute by the given amount. It must never decrease thecreditsUsed
attribute (i.e., it must do nothing when the parameter is negative).The
increasePurchases()
method must increase thepurchases
attribute by the given amount. It must never decrease thepurchases
attribute (i.e., it must do nothing when the parameter is negative).The
purchase()
method must process a purchase of the givenamount
(when it is greater than 0.00), using as many credits as are available when theapplyCredits
parameter istrue
(in which case it must invoke theincreaseCreditsUsed()
method, passing it the appropriate value). It must invoke theincreasePurchases()
method passing it the amount due (i.e., the amount of the purchase less any credits that are used), and must return the amount due. For example, if passed100.00
andtrue
when theAccount
holder has20.00
credits available, it must pass20.00
toincreaseCreditsUsed()
, pass80.00
toincreasePurchases()
and must return80.00
. The amount due must never be less than 0.00, but can be 0.00 (when enough credits are available). The credits used must never be greater than the purchase.The
toCSV()
method must return aString
representation of the purchases and credits used, formatted using a formatString
of"%.2f,%.2f"
.The
fromCSV()
method must set the attributes of the owning object. It may assume that theString
it is passed was created by thetoCSV()
method and must not do any error checking.The
toString()
method must use return aString
representation of the purchases, credits used, and available credit, formatted using a formatString
of"Purchases: %.2f\nCredits Used: %.2f\nCredits available: %.2f"
.
The VIPAccount
Class
The VIPAccount
class is an encapsulation of an exclusive membership
account at BBB. (“VIP” is an acronym for “very important person”.)
In addition to the specifications contained in the
UML class diagram, this class must conform to the following
specifications.
The
visits
attribute must keep track of the number of visits to BBB in which the member made a purchase.The default constructor must initialize all attributes to 0.00.
A
VIPAccount
holder can use the express line whenever anAccount
holder can. AVIPAccount
holder can also use the express line when they have 10 or more visits.A
VIPAccount
holder has the same starting reward percentage as anAccount
holder, however, it can increase. Specifically, for every ten visits, aVIPAccount
holder’s reward percentage increases by 0.01 up to a maximum of 0.15 (i.e., it can bever be larger than 15 percent). For example, aVIPAccount
holder who has made 26 visits will have a reward percentage of 0.03 (i.e., 0.01 + 0.02) and aVIPAccount
holder who has made 371 visits will have a reward percentage of 0.15.Each time the
increasePurchases()
method is invoked, it must increase the number of visits by 1. Of course, it must also increase thepurchases
attribute in the base class.The
toCSV()
must return aString
representation of the result of invoking thetoCSV()
method in the base class followed by the number of visits, formatted using a formatString
of"%s,%d"
.The
fromCSV()
method must set all of the attributes of the owning object. It may assume that theString
it is passed was created by thetoCSV()
method and must not do any error checking.The
toString()
must return aString
representation of the result of invoking thetoString()
method in the base class followed by the number of visits, formatted using a formatString
of"%s\nVisits: %d"
.No method in the
VIPAccount
class may duplicate code in theAccount
class.
Unit Testing
You must write JUnit tests for all of your 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.
As in the past, your unit tests of the input/output methods must make use of round-trip testing. So, you must create a JUnit test that first executes the appropriate write method and then executes the appropriate read method. 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.
Submission
You must submit (using Gradescope) a .zip
file named pa2.zip
that contains:
CSVRepresentable.java
,FileIdentifier.java
,FileProcessor.java
,CSVFileProcessor.java
,Account.java
, andVIPAccount.java
; andYour JUnit tests
packaged appropriately. The io
, membership
, and testing
directories/folders
must be at the top of the .zip
file. (See the previous programming assignment if you
have forgotten how to do this.) Do not submit any .bbb
files.
Your grade will be reduced by 5 points for each submission after the 10th submission. So, you should try to ensure that your code is correct before you submit it the first time. In other words, you should not use Gradescope to check your style, to test your code, or to ensure that your tests cover your code - you should do all of that on your computer before you make any submissions to Gradescope.
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 methods/classes 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:
Criterion | Points | Details |
---|---|---|
Conformance to the Style Guide | 0 | Success Required |
Passing Your Tests | 10 | All or Nothing; Success Required |
Coverage of Your Tests | 20 | Partial Credit Possible |
Correctness | 70 | Partial Credit Possible |
As discussed above, your grade will be reduced by 5 points for each submission after the 10th submission. 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 code that does not conform to the specifications, inelegant code, duplicate code, inappropriate variable names, bad comments, etc.
Recommended Process
At this point in the semester, you should be able to create a good process and use it.
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. Note that you do not need the source code for the
CashRegister
class to answer them.
Suppose you have a
CashRegister
class in which acanUseExpressLine()
message is sent to anObject
namedmember
, which is declared to be anAccount
but can actually be either anAccount
or aVIPAccount
. Both classes have such a method. What code is invoked and why?Suppose you have a
CashRegister
class in which theavailableCredit()
message is sent to anObject
namedmember
, which is declared to be anAccount
but can actually be either anAccount
or aVIPAccount
. The onlyavailableCredit()
method is inAccount.java
so, obviously, that code will be invoked. However, thegetRewardPercentage()
method is then invoked, and this method is implemented in both classes. What code will be invoked and why?Suppose you have a
CashRegister
class in which thepurchase()
message is sent to anObject
namedmember
, which is declared to be anAccount
but can actually be either anAccount
or aVIPAccount
. The onlypurchase()
method is inAccount.java
so, obviously, that code will be invoked. However, theincreasePurchases()
method is then invoked, and this method is implemented in both classes. What code will be invoked and why?Suppose you have a
CashRegister
class that has an overloadedaddToLog()
method, one which is passed anAccount
and of which is passed aVIPAccount
. Suppose further that this method is passed anObject
namedmember
which is declared to be anAccount
but can actually be anAccount
or aVIPAccount
, both of which implement theCSVRepresentable
interface. Which overloaded version ofaddToLog()
will be invoked and why?Why is the
FileProcessor
class declared to be abstract?Will the following fragment compile? Why or why not?
FileProcessor fp; fp = new CSVFileProcessor("temp.csv");
In the previous programming assignment, the
io
package was a subpackage of theproduct
package. In this assignment, theio
package is not inside themembership
package. Why not?