HW8: Big Box Bargains¶
Learning Objectives¶
- Implement interfaces and abstract classes, and apply the concept of specialization
- Demonstrate understanding of polymorphism, including dynamic and static binding.
- Apply knowledge 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.
Starter Code¶
The following zip file contains the starter code, which includes provided classes, sample JUnit test files, and classes with method stubs that you need to implement.
Starter Code Is Incomplete
We're giving you partially completed code stubs to help save time on typing and writing documentation comments. However, some important parts are missing. You’ll need to carefully review all the provided code alongside the UML diagram to fix any mistakes and fill in what's missing.
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. The provided classes are outlined in red. Be sure not to edit those classes.
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).
Note
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:
the fromCSV()
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, provided, do not edit¶
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, provided, do not edit¶
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
encrypt()
method takes a string as parameter and returns an encrypted version by shifting each character forward using a fixed key value (KEY
). It does this by addingKEY
to the Unicode value of each character.
-
The
decrypt()
method reverses the encryption by shifting each character in the parameter backward using the same fixed key value (KEY
) used in theencrypt()
method. -
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 need to test your code using JUnit. Sample JUnit tests have been provided to help you get started, and you are encouraged to expand and build upon them.
Submission¶
- You must submit (using Gradescope)
FileProcessor.java
,CSVFileProcessor.java
,Account.java
, andVIPAccount.java
. - Do not submit JUnit tests or provided
CSVRepresentable.java
,FileIdentifier.java
. - 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.
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 |
Correctness | 100 | 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?