HW4: Violet’s Vending Venture

Image source: vecteezy
Learning Objectives
- Implement a UML diagram for classes.
- Implement method overloading in code.
- Write a program that uses ArrayList for data storage.
- Use enums to model fixed categories with associated values.
- Apply exception handling to enforce data validation and system rules.
Overview
A JMU alum, Violet is starting a business with vending machines at different locations on Campus. She wants the business to be profitable, thus to plan she needs your help writing a Java program that simulates a vending machine interaction. The goal of the program is to calculate the profit made by each individual machine, as well as the total profit from all machines combined.
This vending machine sells multiple types of products, each with its own cost and selling price. The program should handle the logic to calculate profit for each item sold, then total the profits for the whole machine.
The program should then display the individual machine profits and also the total business profit.
The Classes to be Written
The following UML diagram shows all of the classes and relationships between them. Note that dependencies (weak relationships) are omitted from the diagram.
classDiagram
direction TB
class ProductType {
<<enumeration>>
+WATER#40;0.25, 1.00#41;
+SODA#40;0.35, 1.50#41;
+CANDY#40;0.40, 1.25#41;
+CHIPS#40;0.70, 2.00#41;
+COOKIES#40;0.85, 2.25#41;
- cost : double
- price : double
-ProductType(cost: double, price: double)
+getCost() double
+getPrice() double
}
class Product {
+ ROUND_PRICE : int = 25$
- type : ProductType
- name : String
- cost : double
- price : double
+ Product(type: ProductType, name: String)
+ setCustomPrice(price: double)
+ getName() String
+ getType() ProductType
+ getPrice() double
+ getCost() double
+ toString() String
}
class Slot {
+ SLOT_SIZE: int = 10$
- type : ProductType
- products : ArrayList~Product~
+ Slot(type: ProductType)
+ load(product: Product, count: int) int throws IllegalArgumentException
+ nextProduct() Product
+ buyOne() Product
+ toString() String
}
class VendingMachine {
- totalProfit : double$
- machineProfit: double
- slots: Slot[]
+ getTotalProfit() double$
+ resetTotalProfit()$
+ VendingMachine(types: ProductType[])
+ load(slotNum: int, count: int, product: Product)
+ nextProduct(slotNum: int) Product
+ buy(slotNum: int) boolean
+ buy(type: ProductType) boolean
+ getSlotCount() int
+ getMachineProfit() double
+ toString() String
}
class Simulator {
- vmList: ArrayList~VendingMachine~
+ Simulator(vmList: ArrayList~VendingMachine~)
+ addVM(vm: VendingMachine)
+ simulate(pCount: int) double
}
Simulator --> VendingMachine : simulates
VendingMachine *-- Slot : is made up of
Product --o Slot : contains
ProductType <-- Product : has a
Slot --> ProductType : has a
%% VendingMachine ..> Product : sells
%% VendingMachine ..> ProductType : uses
Where to Start?
If you look at the UML diagram, you can see how certain classes depend on others. For example, Product depends on only ProductType, while Slot depends on both Product and ProductType.
Start by coding the class with the fewest dependencies – ProductType.
You will be writing three primary classes: Product, Slot and VendingMachine. The Simulator class will simulate
user interaction with the vending machine, utilizing the functionality of all the core classes together. Finally, the
ProductType enum will represent predefined categories of products, each associated with a default cost and price. It
is used by the Product class to determine pricing and by other classes to enforce type consistency.
All the classes must be in the hw4 package. The getter (accessor) methods for all the classes must return the
appropriate instance (attribute) variable values.
ProductType.java
The ProductType enumeration (more about enum📖) defines the fixed set of product categories available in the vending machine. Each product type is associated with two values:
cost : double– the amount it costs the vending machine company to stock the item.price : double– the amount the customer is charged for the item.
This enum is used throughout the system to categorize products or apply default pricing.
Note that, although it is an enum, the ProductType enum must still have a constructor. The constructor has
parameters of cost and price, and sets the corresponding attributes. This constructor should be private, or better,
omit the visibility of your constructor like so:
public enum ProductType {
...
ProductType(double cost, double price) {
...
}
...
}
The Product Class
The Product class models an individual item type in a vending machine. Each product is associated with a ProductType and also has a name. The default cost and price values are based on the ProductType.
Some products may need a custom price, thus the setCustomPrice() method. 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 getter methods perform the expected operation.
Detailed Design Specifications
-
Product(ProductType type, String name)This constructor takes the type and name as parameters and sets the instance variables appropriately. -
setCustomPrice()takes a custom price and sets it for the product instance. Price should be rounded to the next ROUND_PRICEcents above the amount given if the amount given is not already divisible byROUND_PRICE.
Note: ROUND_PRICE represents the value in cents to which all prices should be rounded. If the price given is not
greater than the associate cost, the price should be the next ROUND_PRICE- divisible-value that is greater than the
cost.
Example
Let’s say we’re setting a custom price of $2.35, but the system only allows prices in multiples of 25 cents (like $2.00, $2.25, $2.50, etc.). Since $2.35 is not a multiple of $0.25, the system needs to round it up to the next closest allowed price.
- $2.35 is between $2.25 and $2.50
- The next allowed price (going upward) is $2.50
So, when you enter $2.35, the setCustomPrice() should convert it to $2.50 to match the required rounding rule.
toString()method will return a String of the instance variables of the class exactly as shown below. Assuming an instance withtype"CANDY",name"M&Ms",costof$1.02, and apriceof$1.25,toString()would return:
CANDY: M&Ms Cost: 1.02 Price: 1.25.
The Slot Class
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.
-
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.
Detailed Design Specifications
In addition to the specifications contained in the UML class diagram, this class must conform to the following specifications.
-
Slot(ProductType type)The Slot() constructor creates an empty array list of products and sets the type. -
load(Product product, int count)checks if the given product’sProductTypematches the slot’sProductType. Loads the slot with up to count products, without exceeding capacity. Use SLOT_SIZE as the default value to fill the slot completely. Return the number of products added accordingly. This method should throw anIllegalArgumentExceptionwith the message"Product type does not match slot product type"if the type of the product you are trying to load does not match the slot’s product type. -
nextProduct()returns a reference to the next product available for purchase, without actually removing it from the slot. If the slot is empty, this method will return null. -
buyOne()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()returns a String exactly like the one below for a slot with 6 M&M products. Items should start with the front slot and end with the rear slot.
SlotCount: 6 of
CANDY: M&Ms Cost: 1.02 Price: 1.25.
CANDY: M&Ms Cost: 1.02 Price: 1.25.
CANDY: M&Ms Cost: 1.02 Price: 1.25.
CANDY: M&Ms Cost: 1.02 Price: 1.25.
CANDY: M&Ms Cost: 1.02 Price: 1.25.
CANDY: M&Ms Cost: 1.02 Price: 1.25.
Hint
Don’t forget to make use of other toString() methods.
The VendingMachine Class
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 getter methods return the appropriate instance variable (attribute) values.
-
totalProfit: double– this models the total profit for all of theVendingMachinestogether. It is the sum of thepriceof everyproductbought from all of the machines minus the sum of thecostof all the products ever put in all of the machines. -
machineProfit: double– this models the long term profit for this particular machine. It is the sum of thepriceof everyproductbought from this machine minus the sum of thecostof all the products ever put in this machine. -
slots: Slot[]– this array models the array of slots in theVendingMachine.
Detailed Design Specifications
In addition to the specifications contained in the UML class diagram, this class must conform to the following specifications.
-
VendingMachine(ProductType[] types)Creates a new VendingMachine with one slot for each ProductType in the given array. Each slot is initialized using the corresponding ProductType at the same index. Thus, the number of slots equals the array length. -
load(int slotNum, int count, Product product)Loads theslotindicated byslotNumwithproductuntil it is full or untilcountis reached. Makes appropriate adjustments tomachineProfitandtotalProfitby subtracting costs from profit values.- Watch out! Recall that the
load()method of theSlotclass may throw an exception. You do not need to handle the exception here; instead, just use thethrowskeyword in the method signature to indicate that this method will “pass on” any exceptions to its caller.
- Watch out! Recall that the
-
nextProduct(int slotNum)Returns a reference to the next availableproductin the indicatedslotornullif theslotis empty. -
buy(int slotNum)Models buying one item from theslotnumber indicated. Makes appropriate adjustments tomachineProfitandtotalProfitby adding thepriceto the profit values. Returnsfalseif there is no product to buy. -
buy(ProductType type)Models buying one item from the firstslotthat matches the indicated product type. Makes appropriate adjustments tomachineProfitandtotalProfitby adding thepriceto the profit values. Returnsfalseif there is no product to buy. -
resetTotalProfit()This method resets thetotalProfitstatic 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. -
getTotalProfit()This method is a simple getter for the statictotalProfitvariable, which must track the total profit accumulated across ALL instances ofVendingMachine, not just this specific one. -
getSlotCount()Returns the number of slots in the vending machine. Note that, internally, the vending machine stores its slots in an array, where each slot can hold a specific product and its quantity. This method returns the length of that array. -
getMachineProfit()Returns the total profit earned by a single vending machine, calculated as the sum of profit for all sold products. -
toString()Returns a String representing theVendingMachine, each loaded slot, themachineProfitandtotalProfitexactly as shown below for a 2-slots with different numbers of loaded products.VendingMachinefilled with 7 Skittles in one slot and 5 Oreos in second, where nothing has been bought (so the profits are negative).
Vending Machine
SlotCount: 7 of
CANDY: Skittles Cost: 0.40 Price: 1.25.
CANDY: Skittles Cost: 0.40 Price: 1.25.
CANDY: Skittles Cost: 0.40 Price: 1.25.
CANDY: Skittles Cost: 0.40 Price: 1.25.
CANDY: Skittles Cost: 0.40 Price: 1.25.
CANDY: Skittles Cost: 0.40 Price: 1.25.
CANDY: Skittles Cost: 0.40 Price: 1.25.
SlotCount: 5 of
COOKIES: Oreos Cost: 0.85 Price: 2.25.
COOKIES: Oreos Cost: 0.85 Price: 2.25.
COOKIES: Oreos Cost: 0.85 Price: 2.25.
COOKIES: Oreos Cost: 0.85 Price: 2.25.
COOKIES: Oreos Cost: 0.85 Price: 2.25.
Total Profit: -7.05 Machine Profit: -7.05.
Hint
Use the existing toString() methods.
The Simulator Class
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.
vmList: ArrayList– models the list of vending machines owned by the company
Detailed Design Specifications
-
Simulator(ArrayList vmList)instantiates aSimulator. -
addVM(VendingMachine vm)adds theVendingMachineindicated byvmto the end of the list of vending machines owned by the company. -
simulate(int pCount)simulates buyingpCountproducts from everyslotof everyVendingMachineowned by the company. Returns thetotalProfitof all of theVendingMachines.
Submission
Part A: Canvas readiness quiz
Read the full Assignment. Don’t skip anything. Understanding the whole picture now will save you from headaches later. Then, complete readiness quiz of this assignment in Canvas.
Part B: Stub-out the Classes and Write the simple Enum ProductType
Awesome
You’re stepping right into the world of Test-Driven Development (TDD). 🙌🏽
Step 1: Get Started
- Inside
hws, make a new folder calledhw4. This will be the home base for everything in this assignment.
Step 2: Write the simple Enum ProductType
- Create an enum
ProductTypewith constants like WATER, CANDY, etc. - Refer to the UML and notes to associate each constant with a cost and price.
Step 3: Stub-out Product class
- Create the
Productclass stubs
Step 4: Stub-out Slot class
- Create the
Slotclass stubs
Step 5: Stub-out VendingMachine class
- Create the
VendingMachineclass stubs
Step 6: Stub-out the Simulator class
- Create the
Simulatorclass stubs
Step 7: Submission
- Submit your enum
ProductType.javaand all stub files(Product.java,Slot.java,VendingMachine.java, andSimulator.java) to Gradescope.
Part C: Write the test classes
Step 1: Download provided tests
- Download provided test files: 📄 ProductTest.java and 📄 SimulatorTest.java. Put them inside
hw4.
Step 2: Read and understand the tests in ProductTest.java and SimulatorTest.java.
Step 3: Write tests for Slot class
- Write the test class for the
Slotclass, name it asSlotTest.java - For each method in Slot, write a corresponding test:1) Test expected outcomes; 2)Include edge cases (e.g. max and min, null values, etc.); and 3) Use assertEquals, assertTrue, etc., to verify behavior
Step 4: Write tests for VendingMachine class
- Write the test class for the
VendingMachineclass, name it asVendingMachineTest.java
Step 5: Submission
- Submit your tests files(
SlotTest.javaandVendingMachineTest.java) to Gradescope.
Part D: Implement all classes
Step 1: Implement and Test the Product Class
- Before you implement the logic and run anything, grab a pencil and paper and manually calculate what the expected outputs of ProductTest should be. This will help you truly understand how the Product class is supposed to behave.
- Now write the actual logic in your Product class methods based on what you’ve learned. Make it do what it’s supposed to do!
- Run
ProductTest. If any tests fail, dive into the relevant method(s) in Product.java and figure out what’s going wrong. Here is how to run tests in VSCode! - Fix the bug, run the test again, and keep iterating until all tests in
ProductTestpass.
Step 2: Implement and Test the Slot Class
- Start coding each method according to the design.
- Run SlotTest and check the results.
- If any test fails, inspect both your test logic and
Slotclass implementation to find the bug. - Tweak your code or tests as needed, then rerun everything until all tests pass and you’re confident it works.
Repeat similar steps for VendingMachine and Simulator
You must submit (using Gradescope): the Product class, the Slot class, the VendingMachine class and the Simulator class.
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 (Style) | 0 points | (All or Nothing: Success Required on all parts) |
| Compilation | 0 or 5 points | (All or Nothing: Success Required on all parts) |
| Part A: Canvas readiness quiz | 10 points | (All or Nothing: Success Required) |
| Part B: Stubs and ProductType | 20 points | (Compilation Required 5 points, Partial Credit Possible on ProductType) |
| Part C: Runs your tests against our reference solution | 10 points | (Partial Credit Possible) |
| Part C: Passing Your Tests (SelfTests) | 10 points | (Partial Credit Possible) |
| Part D: Correctness (Official Tests) | 50 points | (Partial Credit Possible) |
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.
- How does using the ProductType enum improve the design of the Product class compared to using strings or integers?
- What is the benefit of separating the logic into multiple classes like Product, Slot, and VendingMachine instead of
- putting all code into one class?
- What rule should setCustomPrice() in Product enforce so that the assigned price always aligns with the ROUND_PRICE
- and stays consistent with system pricing rules?
- How can the load() method safely prevent loading an incorrect product type in a Slot?
- How do the buy() methods relate to machineProfit and totalProfit in VendingMachine?
- Could you reuse the VendingMachine class in a different project? Why and how or why not?
- When writing tests for Slot and VendingMachine, what helped you better understand how the classes were supposed to
- behave?