Project 3 - Violet's Vending Venture
15 minute read
Introduction
Your friend Violet and her family are considering starting a vending machine business. To make sure it will be profitable, they have decided to keep it simple and plan! The planning part is where you get involved. She wants you to create a program that will allow her to simulate some of the possibilities of their business decisions. The goal is to have a program that represents the different kinds of vending machines they currently intend to operate with the possibility of adding different machine types in the future. The program will simulate the machines and model the profit of each machine and the whole business (the sum of the profit of all the machines). You both realize that this means a program that uses inheritance to represent different kinds of vending machines which will make the code simpler, more flexible, and easier to maintain.
Together you decide that you will largely model profit. First to consider is the individual machine profit so that you can tell if individual machine locations are profitable. Second is the total profit of the whole business. Violet also decides that she wants to model a basic machine with only one kind of product that doesn't require anything special to operate first. But, she will consider two other kinds of machines that have a bit more sophistication in their functionality. Specifically she will want to add a drink machine, where the whole machine is still filled with the same product, but now each product will have an added cost charged to it for the refrigeration. Both the basic machine and the drink machine will require exact change. The last machine she wants to model is a traditional snack machine. It requires no refrigeration, but each slot in the machine will be loaded with a different snack product which could have a different cost, and it will return change when something is bought. In support of this vision, you have come up with the UML diagram below.
Submission and Honor Code
Submissions should be made by the due dates listed on Canvas. There are no late submissions unless specified by your instructor otherwise. The Gradescope results will reflect the no late submissions policy. Only the latest submission to Gradescope will be graded and code that does not compile will receive a 0, so please be careful with last minute submissions. Your final grade will be determined largely by correctness as determined by your Gradescope score, but documentation, and overall code quality will also be considered.
Though you are encouraged to discuss concepts with peers, teaching assistants and the instructor, the source code you submit should be your own work and something you could replicate on your own. Representing someone (or something) else's work as your own, in any form, constitutes an honor code violation. Directly copying or using the same code is an honor code violation. It is also a violation of the honor code to "render unauthorized assistance to another student by knowingly permitting him or her to copy all or a portion of an examination or any work to be submitted for academic credit." That means that you can be written up for an Honor code violation if you share your code with someone else, even if you wrote it completely on your own. If you violate the University Honor Code, you will receive a reduced or failing grade for the assignment or other penalties may be imposed and the violation will be reported to the Honor Council.
Individual instructors may have additional requirements. Please consult the syllabus for your section for details.
Learning Outcomes
- Implement a UML diagram that uses inheritance.
- Correctly implement examples of overriding and overloading.
- Correctly predict via simulation the interaction of objects from an inheritance hierarchy.
- Implement a program that makes use of ArrayLists.
UML Diagram
The connectors in the UML diagram below now include inheritance. Recall, this tutorial: UML Tutorial
Implement the class and methods according to the UML and specifications below.
Part 1 - Create Product.java
and ProductTest.java
The Product class models an individual item type in a vending machine. Each product has a name, cost. and price. Note that cost is the cost of the product to the vending machine company. Price is the price that the vending machine will charge the customer. Violet has said that she is unwilling to deal with anything but quarters, dollar bills, and debit cards so all prices are kept divisible by 25 cents. Costs to the vending machine company can be any penny value. All of the get methods perform the expected operation.
Data
Note: cost and price are both integers. All money in the vending machine is represented as cents to enable simpler math and eliminate rounding issues.
ROUND_PRICE: int
- the value in cents to which all prices will be roundedname: String
- the name of the product typecost: int
price: int
Product()
The default constructor will create an instance of a product with a name of "Generic"
, a cost of ROUND_PRICE = 25
cents
and a price of twice the ROUND_PRICE
.
Product(String name, int cost, int price) throws IllegalArgumentException
This constructor takes the name
, cost
, and price
as parameters and sets the instance variables appropriately.
Null string names or negative cost or price should throw an IllegalArgumentException. Prices should be rounded to the next ROUND_PRICE
cents above the amount given if the amount given if not already divisible by ROUND_PRICE
.
Note: if the price given is not greater than the cost, the price should be the next ROUND_PRICE
-divisible-value that is greater than the cost.
toString()
The toString() method will return a String of the instance variables of the class exactly as shown below. Assuming a name of "M&Ms"
,
cost of $1.02
, and a price of $1.25
, toString()
would return:
Product: M&Ms Cost: 1.02 Price: 1.25.
Note: the cost and price are kept in cents so the toString()
method will need to transform the values into the right format.
Part 2 - Create Slot.java
and SlotTest.java
The Slot
class models a slot in the vending machine. Slots are loaded from the rear, and purchases are removed from the front.
This ensures that the items that have been in the machine the longest are sold before newly added items.
Data
SLOT_SIZE: int = 10
- the maximum number of products that a slot in the vending machine can hold.products: ArrayList<Product>
- models the actual products that are in the slot. Removing the one at the front models buying one of the products in the slot and all of the others are moved forward similar to an actual vending machine.
Slot()
The Slot()
constructor creates an empty array list of products.
Slot(Product product)
This constructor creates a new slot
that is filled with SLOT_SIZE
of
product
.
load(Product product)
This method loads the slot
with however many new product
s are required to
make sure it is full and returns the number of product
s it took to fill the slot
.
load(Product product, int count)
This method loads the slot
with up to count new product
s in an
attempt to fill the slot
and returns the number of product
s it used.
nextProduct()
This method returns a reference to the next product
available for purchase. If the slot
is empty this method
will return null
.
buyOne()
This method simulates the purchase of one item from the perspective of the slot
. That means no money is dealt with here,
rather the slot
is checked to make sure there is product
to buy and then one product
is removed
from the front of the ArrayList
modeling the slot
. If a product
is successfully removed from the
slot
, it is returned, otherwise null
is returned.
toString()
The toString()
method returns a String
exactly like the one below for a slot
with 10 M&M products.
Items should start with the front slot and end with the rear slot.
SlotCount: 10 of Product: M&Ms Cost: 1.02 Price: 1.25. Product: M&Ms Cost: 1.02 Price: 1.25. Product: M&Ms Cost: 1.02 Price: 1.25. Product: M&Ms Cost: 1.02 Price: 1.25. Product: M&Ms Cost: 1.02 Price: 1.25. Product: M&Ms Cost: 1.02 Price: 1.25. Product: M&Ms Cost: 1.02 Price: 1.25. Product: M&Ms Cost: 1.02 Price: 1.25. Product: M&Ms Cost: 1.02 Price: 1.25. Product: M&Ms Cost: 1.02 Price: 1.25.
Hint 😉
Hint: Don’t forget to make use of othertoString()
methods.Resources
- ChangeMakingMachineTest.java
- DrinkMachineTest.java
- SimulatorTest.java
- SnackMachineTest.java
- TestingConstants.java
- VendingMachineTest.java
Part 3 - Create VendingMachine.java
and test it.
The VendingMachine
class is a simple vending machine. Exact change is required so it is assumed if someone is buying something
they have inserted the right amount of money or have used a debit card. The get
methods return the appropriate instance variable values.
Data
DEFAULT_SIZE
:int = 15
– the default size for aVendingMachine
, used primarily by the default constructortotalProfit
:int
– this models the total profit for all of theVendingMachine
s together. It is the sum of theprice
of everyproduct
bought from all of the machines minus the sum of thecost
of all theproduct
s ever put in all of the machines. Note that it isprotected
in the UML diagram so it is accessible to classes that inherit from this class.machineProfit
:int
– this models the long term profit for this particular machine. It is the sum of theprice
of everyproduct
bought from this machine minus the sum of thecost
of all theproduct
s ever put in this machine. Note that it isprotected
in the UML diagram so it is accessible to classes that inherit from this class.slots
:Slot[]
– this array models the array ofslot
s in theVendingMachine
.
VendingMachine()
The default constructor creates a VendingMachine
with DEFAULT_SIZE
empty Slot
s.
VendingMachine(int size)
Creates a VendingMachine
with the indicated number of empty Slot
s.
VendingMachine(int size, Product product)
Creates a VendingMachine
with size
Slot
s each full of product
.
load()
Loads an empty or partially empty VendingMachine
with a Generic product (i.e. the product obtained using the default constructor of the Product class.)
Makes appropriate adjustments
to machineProfit
and totalProfit
by subtracting cost
s from profit
values.
load(int slotNum, int count, Product product)throws IllegalArgumentException
Loads the slot
indicated by
slotNum
with product
until it is full or until count
is reached. Makes appropriate adjustments to
machineProfit
and totalProfit
by subtracting costs from profit values. Throws an IllegalArgumentException
if the
slotNum
is out of bounds, the count
is less than or equal to zero, or if the product
is null
.
nextProduct(int slotNum)throws IllegalArgumentException
Returns a reference to the next available product in the indicated slot or null if the slot is empty. Throws an IllegalArgumentException
if the slotNum is out of bounds.
buy(int slotNum)throws IllegalArgumentException
Models buying one item from the slot number indicated. Makes appropriate adjustments to machineProfit
and totalProfit
by adding the price
to the profit
values. Throws an
IllegalArgumentException
if the slotNum
is out of bounds. Returns false
if there is no product to buy.
resetTotalProfit()
This method resets the totalProfit
static instance variable to zero. This is useful when testing to make sure that different method tests
start out in a known state for the static variable so the final value can be computed without knowing the order of the test runs.
toString()
Returns a String
representing the VendingMachine
, each slot
, the machineProfit
and
totalProfit
exactly as shown below for a 2-slot VendingMachine
filled with Generic product
where nothing
has been bought (so the profit
s are negative).
Vending Machine SlotCount: 10 of Product: Generic Cost: 0.25 Price: 0.50. Product: Generic Cost: 0.25 Price: 0.50. Product: Generic Cost: 0.25 Price: 0.50. Product: Generic Cost: 0.25 Price: 0.50. Product: Generic Cost: 0.25 Price: 0.50. Product: Generic Cost: 0.25 Price: 0.50. Product: Generic Cost: 0.25 Price: 0.50. Product: Generic Cost: 0.25 Price: 0.50. Product: Generic Cost: 0.25 Price: 0.50. Product: Generic Cost: 0.25 Price: 0.50. SlotCount: 10 of Product: Generic Cost: 0.25 Price: 0.50. Product: Generic Cost: 0.25 Price: 0.50. Product: Generic Cost: 0.25 Price: 0.50. Product: Generic Cost: 0.25 Price: 0.50. Product: Generic Cost: 0.25 Price: 0.50. Product: Generic Cost: 0.25 Price: 0.50. Product: Generic Cost: 0.25 Price: 0.50. Product: Generic Cost: 0.25 Price: 0.50. Product: Generic Cost: 0.25 Price: 0.50. Product: Generic Cost: 0.25 Price: 0.50. Total Profit: -5.00 Machine Profit: -5.00.
Part 4 - Create DrinkMachine.java
and test it.
The drink machine inherits from the general VendingMachine
described above. The only additions are a constant for the cooling charge
and a different buy
method which will affect the profit for the machine and the total profit differently than in the general
VendingMachine
. DrinkMachine
s will assess a charge for keeping the drink cold when the drink is purchased.
This charge impacts the cost of the product to the vending machine company. It does not impact the price for the customer.
Data
COOLING_CHARGE
:int = 10
– this models the ten-cent charge assessed to each drink when it is purchased to account for the refrigeration costs of the drink machine.
DrinkMachine()
Performs the same action for the DrinkMachine
as VendingMachine()
.
DrinkMachine(int size, Product product)
Performs the same action for the DrinkMachine
as VendingMachine(int size, Product product)
.
buy(int slotNum) throws IllegalArgumentException
Models buying one item from the slot number indicated. Throws an IllegalArgumentException
if the slotNum
is out-of-bounds.
Makes appropriate adjustments to machineProfit
and totalProfit
by adding the price
Hint 😉
use apublic
method) minus the COOLING_CHARGE
to the profit
values.Part 5 - Create ChangeMakingMachine.java
and test it.
The change-making machine will add the functionality of being able to pay with cash and potentially get change back. The change will just be returned as an integer value in cents, but the amount paid in will be determined by the number of quarters and dollars that are entered.
ChangeMakingMachine()
Performs the same action for the ChangeMakingMachine as VendingMachine()
ChangeMakingMachine(int size)
Performs the same action for the ChangeMakingMachine as VendingMachine(int size).
ChangeMakingMachine(int size, Product product)
Performs the same action for the ChangeMakingMachine as VendingMachine(int size, Product product)
buy(int slotNum, int quarters, int dollars)throws IllegalArgumentException
Models buying one item from the slot number indicated. Throws an IllegalArgumentException
if the slotNum
is out of
bounds or if quarters or dollars are negative. Computes the amount of money put into the machine in quarters and dollars, returning -1 if there
is not enough money to buy the product and returning the positive difference or “change” if there is any. Makes appropriate
adjustments to machineProfit
and totalProfit
by adding the price to the profit values if the buy is successful.
⚠️ Note
Use a public method to accomplish this.Part 6 - Create SnackMachine.java
and test it.
The snack machine will inherit from the ChangeMakingMachine. The difference is it will have an additional instance variable of an array list of products which will indicate the type of product each slot should contain and it’s load method will fill each slot completely with the particular product the slot contains, making appropriate adjustments to the profit tallies.
Data
productList
:ArrayList
– contains the list ofproduct
s for eachslot
in theSnackMachine
. The first position in theproductList
corresponds to theproduct
in the firstslot
in theslots
array.
SnackMachine(ArrayList pList)
This constructor initializes the productList
of the SnackMachine
and creates a new snack machine where each
slot
is full of the product indicated in the matching position in the productList
. The size
of the snack
machine is just the length of the productList
.
load()
This load method completely fills each slot
in the snack machine with the appropriate product
. As a slot
is filled, the total cost
of the items is subtracted from the profit
tallies.
Part 7 - Create Simulator.java
and test it.
The simulator will provide a means of simulating a small business of vending machines. The vending machines of the business are stored in an array list. Vending machines can be added to the list using the provided method to simulate the growth of a business. Simulation of the business “running” and selling product is done by simulating a specific number of products being bought from every slot of every vending machine in the business and returning the totalProfit of all of the vending machines in cents.
Data
- vmList: ArrayList – models the list of vending machines owned by the company
Simulator(ArrayList vmList)
Instantiates a Simulator
addVM(VendingMachine vm)
Adds the VendingMachine
indicated by vm
to the end of the list of vending machines owned by the company.
simulate(int pCount)
Simulates buying pCount
product
s from every slot
of every VendingMachine
owned by the company.
Returns the totalProfit
of all of the VendingMachines
.
Submission
Submission for this assignment is divided into two parts that should be completed in order.
- PA3-A (10%): Readiness Quiz
- In order to complete Part A you should first carefully read the project specification. Once you feel confident that you have a good grasp of the project requirements, log into Canvas and complete the Part A quiz. YOU MUST ANSWER ALL QUESTIONS CORRECTLY TO GET ANY CREDIT FOR THIS PART. You may take the quiz as many times as necessary.
- PA3-B (25%): For this part you must submit your
Product.java
,ProductTest.java
,Slot.java
, andSlotTest.java
to Gradescope. Your test classes should provide 100% branch coverage for the classes they test. For this part you will have unlimited submissions. - PA3-C (65%): For this part you must submit .java files for all of the classes,
Product.java
,Slot.java
,VendingMachine.java
,DrinkMachine.java
,ChangeMakingMachine.java
,SnackMachine.java
, andSimulator.java
files to Gradescope. Note that you will need to use the test classes provided under Resources above to test your code before submitting to Gradescope. There will be a deduction of 1%/ea for every submissions beyond ten.
Code submitted for Parts B and C must conform to the course style guide.
Submission Penalties
Part C will include a penalty for excessive Gradescope submissions. You are allowed ten submissions with no penalty. There will be a deduction of 1%/ea for every submissions beyond ten.
Recommended Process
- Follow the order of the parts as outlined above.
- Develop incrementally, testing as you go.
- As you build up to Test-Driven Development, you should supplement your efforts with at least a
main
method (either in one of the classes you’re already writing or perhaps better in a separate “driver class”). Since Part 1 says to create the Product class, your driver class should have something that tests whether this is working: // you must know by know that you're not going to get me to write the main method for you // especially when eclipse is so eager to help Product p = new Product("Coke", 75); Product q = new Product(); System.out.println(p.getName()); // aside from this being a nice thing to print System.out.println(p.getPrice());// these lines might also make good places System.out.println(q.getName()); // to put breakpoints so that you can use the debugger inspect System.out.println(q.getPrice());// the Product objects created by your constructors
- As you build up to Test-Driven Development, you should supplement your efforts with at least a