Vending Machine (Java)
From LiteratePrograms
This is a Java program which simulates the behaviour of a vending machine that issues drinks and food to customers.
- The price of all of the items that are in a given machine is the same.
- Users pass values to the program which represents users inserting coins to purchase an item.
- The vending machine keeps note of the amount of money it has received since it was initiated and the balance of a customer's coins.
- Customers can have a refund of their money.
This is a good program for learning the basics of Java, as it introduces the ideas of fields, local variables, constructors, accessor and mutator methods and conditional statements.
Defining the Class
When naming a class it is important to have a realistic name so it is easy to identify what the class does (to yourself and others who may be viewing the program). It is not a good idea to give the class a name such as MyClass as it tells us nothing about a class and its behaviour. This class is called VendingMachine, as it identifies the behaviour described above.
<<VendingMachine.java>>= public class VendingMachine { class body }
Declare the Vending Machine Fields
We first define the fields that our class will use to do its work. Fields essentially store data for each object to use. As the values of these fields are going to vary over time they are known as variables. The three variables that VendingMachine is going to be using are:
<<Declare the vending machine fields>>= private int itemPrice;
which stores the price of a can of fizzy drink;
<<Declare the vending machine fields>>= private int currentBalance;
which stores the amount of money in pence that the customer has entered before ordering the can of fizzy drink; and
<<Declare the vending machine fields>>= private int totalCollected;
which stores a record of the amount of money in pence that the machine has received since it has been initialized.
Notice that the each of these fields are private. This means that they can only be used in this class and are not visible to other classes. They are also of type int, or integer, which means they can store single whole number values, which is handy because we are dealing with values that represent coins which of course have whole number values.
Creating new vending machines - the Constructor
After successfully defining the variables that will be used in the class we must use them to simulate the behaviour of a vending machine. Firstly we must set up the variables used by the class. This is also known as initialization and is performed in the constructor of our VendingMachine class.
The initialisation of the currentBalance and totalCollected fields is straightforward as we simply wish to give them a sensible starting value. Because a newly installed vending machine will initially contain no money, a suitable initial value in in this case is zero:
<<Initialise fields>>= currentBalance = 0; totalCollected = 0;
The itemPrice field, however, is not so simple. Because we don't know in advance the price that a given organization will want to charge for an item from its vending machine, we must wait until a machine has been "installed" before we can set the initial value of the itemPrice field. It is also possible that we may wish to create more than one machine that issues a different item at a different price. This means that we can't initialize the value of itemPrice to a given set value. We have to defer this decision until we install the machine. In terms of our simulation, this means that we want to set the price when we create a new vending machine object:
<<test vending machine>>= VendingMachine sodaVendor = new VendingMachine(75); VendingMachine candyBarVendor = new VendingMachine(60);
That is the price of the item is determined when we create each VendingMachine object, and then we pass the price into the VendingMachine object as an argument to the VendingMachine constructor.
Note that the constructor has the same name as the class that it is defined in, which, in this case in VendingMachine. The constructor code is declared using:
<<Construct a vending machine>>= public VendingMachine(int itemCost) { Initialize fields }
The value that is to be passed as a parameter, is defined in the header of the constructor. The parameter itemCost, is of type int, which is the same type as the itemPrice field it is going to be setting.
The parameter then needs to be stored into the itemPrice field. The process of storing a parameter into a field is known as assignment:
<<Initialize fields>>= itemPrice = itemCost;
The assignment works by copying the value of the itemCost parameter into the itemPrice field.
Now we have defined and initialised the variables that we are going to be using in the VendingMachine class we must write methods that simulate the actions that a typical vending machine would perform.
Returning Price
If we are going to be buying items from the machine we need to know how much they cost. The first method of the VendingMachine class is called getItemPrice and tells us exactly this. It is used like this:
<<test vending machine>>= System.out.println("A can of soda costs " + sodaVendor.getItemPrice() + " pence."); System.out.println("A candy bar costs " + candyBarVendor.getItemPrice() + " pence.");
and we would expect the result to be:
<<test results>>= A can of soda costs 75 pence. A candy bar costs 60 pence.
Method declarations have two parts called the header and the body:
<<Return item price>>= Method header Method body
The method header is the method signature. Like the class name it is a good idea to give it a name that identifies what it does. In this case we have called it getItemPrice:
<<Method header>>= public int getItemPrice()
The int return type in the method signature indicates that the method body will eventually return an integer as its result. This type of method is known as an accessor method, because it returns information about the state of an object, or provides access to that state.
The body of a method contains declarations and statements that determine what happens to an object when the method is called. The body of a method is always enclosed in curly brackets { and }:
<<Method body>>= { Return the price }
In this example, the statements in the body are very simple because we only want to return the value currently held by the field itemPrice. And to achieve this, we simply return that value!
<<Return the price>>= return itemPrice;
The return statement returns an integer value that matches the int method type that is defined in the method signature.
Returning the Current Balance
The next method in the VendingMachine class is also an accessor method. When a customer is buying an item from a vending machine it is useful for them to see how much money they have put into the machine so they know if they have put in enough to purchase an item. It will be used like this:
<<test vending machine>>= sodaVendor.insertCoin(50); System.out.println("The current balance in the soda vendor is: " + sodaVendor.getCurrentBalance() + " pence."); sodaVendor.insertCoin(20); System.out.println("The current balance in the soda vendor is now: " + sodaVendor.getCurrentBalance() + " pence.");
And the expected result should be:
<<test results>>= The current balance in the soda vendor is: 50 pence. The current balance in the soda vendor is now: 70 pence.
To implement the method getCurrentBalance we use the same pattern
as for the getItemPrice method:
<<Return current balance>>= public int getCurrentBalance() { return currentBalance; }
Again, the body of this method is very basic. It simply returns the current value of the currentBalance field.
Inserting Money
The next method in the VendingMachine class is more significant in simulating the behaviour of a vending machine. The two previous methods only return information about a vending machine's fields. But there are times when we will need to alter the value of the fields in the VendingMachine class if we are to realistically simulate a vending machine. A method that changes the value of a field is known as a mutator method.
The basic process of using a vending machine involves inserting enough money to be able to select a product. The next method in the VendingMachine class emulates the first stage of the process, inserting money.
We have already seen the method that will be used to insert money insertCoin(), the signature of this method must therefore be:
<<Insert a coin>>= public void insertCoin(int faceValue) { Insert a coin: body }
The first noticeable difference between this method and the previous two methods is the method signature. This method has a void return type, which means that it doesn't return any value to its caller. It also has a single parameter faceValue, which is of type int. This parameter is used to represent the value of a coin that has been put into the machine. When we enter a coin, we simply need to add faceValue to the current balance:
<<add the value of a coin to the current balance>>= currentBalance = currentBalance + faceValue;
Dispensing Items
When we have inserted enough money we wish to receive an item from the machine. The next method in the VendingMachine class mimics this.
"Vending" an item
<<test vending machine>>= sodaVendor.insertCoin(5); // now have 75p sodaVendor.dispenseItem();
If the method contains the following code:
<<simulate the vending of an item>>= System.out.println("#######################################"); System.out.println("# Thank you for your custom."); System.out.println("# Your item has been released. "); System.out.println("# Your item cost " + itemPrice + " pence."); System.out.println("#######################################"); System.out.println();
then the soda vending machine should print:
<<test results>>= ####################################### # Thank you for your custom. # Your item has been released. # Your item cost 75 pence. #######################################
Defining the method
The dispenseItem method is, again, of void return type but this time it also has no parameters (the parenthesis () is empty):
<<Dispense an item>>= public void dispenseItem() { Dispense an item: body }
As the VendingMachine class is storing the amount of money that it has received, it is important that there is a statement somewhere in the class that updates the value of totalCollected. It makes sense to do this at the end of the transaction as we know there won't be any more money put in until someone wishes to buy another item. The assignment statement achieves this by replacing the value of totalCollected with the result of the sum of totalCollected and itemPrice.
<<simulate the vending of an item>>= totalCollected = totalCollected + itemPrice;
Updating the fields
The user has now spent some of their money on an item so the currentBalance variable must be updated. This assignment statement achieves this by replacing the value of currentBalance with the value of currentBalance less the value of itemPrice.
<<simulate the vending of an item>>= currentBalance = currentBalance - itemPrice;
In our test, because the cost of a soda is 75 and we have entered coins to the value of 75, the current balance should be 0:
<<test vending machine>>= System.out.println("The current balance is the soda vendor is now: " + sodaVendor.getCurrentBalance() + " pence.");
<<test results>>= The current balance is the soda vendor is now: 0 pence.
Using conditionals
There is an implicit conditional statement involved in this method. The vending machine should only vend an item if the current balance is greater or equal to the item price. Thus we need a check to see if the user has entered enough money to purchase an item. We implement this as follows.
- If the value of
currentBalanceis equal to or exceeds theitemPrice, an item will be vended. - If the
currentBalanceis less than theitemPrice, theelsestatement is executed. - The
elsestatement tells the user how much more money to insert into the machine. The extra needed is calculated by subtractingitemPricefromcurrentBalance.
Here is the code:
<<Dispense an item: body>>= if (currentBalance >= itemPrice) { simulate the vending of an item } else { System.out.println("Please insert more coins to at least a value of " + (itemPrice - currentBalance) + "."); }
To test this, let us try to buy some candy:
<<test vending machine>>= candyBarVendor.insertCoin(50); candyBarVendor.dispenseItem(); // should fail - 10 short! candyBarVendor.insertCoin(50); candyBarVendor.dispenseItem();
The results should be:
<<test results>>= Please insert more coins to at least a value of 10. ####################################### # Thank you for your custom. # Your item has been released. # Your item cost 60 pence. #######################################
Refunding Money
If any money remains in the balance after a successful transaction is completed, it could be used to purchase further items. Alternatively, the customer may wish to be refunded with any remaining balance.
<<test vending machine>>= // balance should now be 40! System.out.println("Candy bar vendor balance is now: " + candyBarVendor.getCurrentBalance() + " pence.");
The test results for this should be:
<<test results>>= Candy bar vendor balance is now: 40 pence.
If we want to refund the balance, the code we need is:
<<Refund coins>>= public int refundCurrentBalance() { calculate refundable amount and adjust balance return refundableAmount; }
This method is of return type int, which means that it is eventually going to return a value that is of integer type. It also uses a type of variable that has not been used in this class yet refundableAmount, which is a local variable.
Local variables
Local variables can be used as temporary storage locations, which aid the method in completing its task. In this case, it is used to store the value of currentBalance before currentBalance is set to 0.
<<calculate refundable amount and adjust balance>>= int refundableAmount; refundableAmount = currentBalance; currentBalance = 0;
Because we are returning currentBalance it could not be set to 0 after the return statement as return statements cause the execution of a method to end. By using the local refundableAmount variable to store the value of currentBalance we can set currentBalance to 0 before the returning its old value in the form of refundableAmount.
Let us refund the money that we have in the candy bar vending machine:
<<test vending machine>>= int refund = candyBarVendor.refundCurrentBalance(); System.out.println("Is refund equal to 40? [true/false]: " + (refund == 40) + "."); System.out.println("Current balance in the candy bar vending machine is now: " + candyBarVendor.getCurrentBalance() + ".");
We would expect this to print:
<<test results>>= Is refund equal to 40? [true/false]: true. Current balance in the candy bar vending machine is now: 0.
More Conditionals: Handling Possible Useage Errors
Because we have simulated a coin by a simple integer, it is possible for a user to enter a negative (or even a zero) value as in:
<<test vending machine>>= sodaVendor.insertCoin(1); // OK sodaVendor.insertCoin(0); // Invalid sodaVendor.insertCoin(-1); // Invalid
Neither of these possibilities make any sense in this context. To correct for this we use another conditional statement. This time to validate the parameters passed to the insertCoin method. If the faceValue of the coin is a positive number we add its value to the current value. Otherwise, we print an error message:
<<test results>>= Invalid coin with face value 0 inserted. Use a positive value! Invalid coin with face value -1 inserted. Use a positive value!
Here is the required code:
<<Insert a coin: body>>= if(faceValue > 0) { add the value of a coin to the current balance } else { System.out.println("Invalid coin with face value " + faceValue + " inserted. Use a positive value!"); }
Note that if a user of the VendingMachine class does insert an invalid coin, the process of updating currentBalance is skipped as this is undesirable. In other words, currentBalance is not changed. To verify that this is what happens we write another test:
<<test vending machine>>= System.out.println("Current balance in soda vendor is: " + sodaVendor.getCurrentBalance() + " (should be 1).");
which should result in: <<test results>>= Current balance in soda vendor is: 1 (should be 1).
The Complete Class
Putting all these methods together, the body of the class is simply:
<<class body>>= Declare the vending machine fields Construct a vending machine Return item price Return current balance Insert a coin Dispense an item Refund coins /* Test the class */ Main method
Exercises
- Add a new accessor method
getTotalCollectedthat displays the current value of thetotalCollectedfield. Test that when an item is vended, the total increases by the cost of the vended item. - Write a method
emptyMachinethat simulates the emptying of the vending machine: that is it should return the total collected so far and then zero thetotalCollectedfield. Test your new method. - What should happen if the machine is emptied whilst there is a current balance? Does the
VendingMachinework this way? Test it, and if necessary correct it! - Add a new field
String itemNameto record the name of the item that a vending machine can issue. Use the constructor to set the vended itemVendingMachine sandwichVendor = new VendingMachine("Chicken salad sandwich", 275);. Write an accessor method that returns the item name. Test your new constructor and accessor method. - Create a method
public String describeItem()that prints the name and price of an item. This method can be used in print statements like <ocde>System.out.println(sandwichVendor.decribeItem())</code> to printChicken salad sandwich (275). Implement the method using the existinggetItemPrice()and the newgetItemName()method. Modify the tests to use this new method in print statements. - (Advanced) Change the
insertCoinso that it will only accept coins with face value of 1, 2, 5, 10, 20, and 50 units. Print a messages such as "Coin rejected" if a coin with any other value is inserted. Write tests to verify that your modified method works. - (Advanced) How might you create storage for each of the valid coins in the previous question? How would you need to change the
getTotalCollectedmethod if you stored coins in this way?
Acknowledgments
This example is fairly strongly influenced by an example from Barnes and Kölling's book Objects First with Java: A Practical Introduction Using BlueJ, 2nd Edition, Prentice Hall, 2002.
See Also
Testing Vending Machine (Java, Junit) is an article which explains how to use the JUnit test framework to test this vending machine class and also acts as a tutorial for JUnit.
Appendix: Testing the Code
The Main Method
To run the class as a standard Java application you will need a main method (See Hello World (Java)). In this example, we use the main method to run the vending machine tests.
<<Main method>>= public static void main(String[] args) { test vending machine }
The results of executing java VendingMachine should be:
<<results.txt>>= test results
You should compare your program's output against the contents of results.txt. (Note that because of the way that this Wiki handles white space, your output may have more or less blank lines in its output.)
Testing in BlueJ
BlueJ is a widely used simple Interactive Development Environment (IDE) that is used by many universities in introductory programming courses. It emphasises and supoorts an objects-first approach in which students learn about classes and objects by using direct manipulation. programs written for BlueJ do not need a main method can be tested without the need for System.out.println().
Metadata for a BlueJ project
The following pieces of metadata make the contents extracted from the Download Code page compatible with a BlueJ project file. You can simply download and extract the zip file (windows) or tar.gz file (other platforms) into a folder and that folder will automatically become a BlueJ project file.
This text file acts as a marker which identifies the code as belonging to a BlueJ project.
<<bluej.pkg>>= #BlueJ package file
This provides a README file that programmers can read to find out more about the project.
<<README.TXT>>= This is a [http://en.wikipedia.org/wiki/Java_programming_language Java] program which simulates the behaviour of a vending machine that issues drinks and food to customers. * The price of all of the items that are in a given machine is the same. * Users pass values to the program which represents users inserting coins to purchase an item. * The vending machine keeps note of the amount of money it has received since it was initiated and the balance of a customer's coins. * Customers can have a refund of their money. This is a good program for learning the basics of Java, as it introduces the ideas of ''fields'', ''local variables'', ''constructors'', ''accessor'' and ''mutator methods'' and ''conditional statements''.
Testing your class in BlueJ
Because you can instantiate objects in BlueJ and invoke their methods directly, testing is slightly different. The code that you'll find in the main can be used as a script for testing. However, you do not need to use System.out.println() to display the results of calling the methods. Simply call the methods directly using BlueJ's interactive features.
Here's the testing script for BlueJ adapted for its features:
- Invoke
new VendingMachine(int itemCost);on theVendingMachineclass, make the name of the instancesodaVendor, and set theitemCostto 75. - Invoke
new VendingMachine(int itemCost);on theVendingMachineclass, make the name of the instancecandyBarVendor, and set theitemCostto 60. - Invoke
getItemPrice()on thesodaVendorobject: the result should be 75. - Invoke
sodaVendor.getItemPrice(): the result should be 60. - Invoke
sodaVendor.insertCoin(50). - Invoke
sodaVendor.insertCoin(20). - Invoke
sodaVendor.getCurrentBalance(): the result should be 70. - Invoke
sodaVendor.insertCoin(5): the balance should now be 75. - Invoke
sodaVendor.dispenseItem(). The system should simulate vending a can by writing a "receipt" to the Terminal Window. - Invoke
sodaVendor.getCurrentBalance(): the result returned should be 0. - Invoke
candyBarVendor.insertCoin(50). - Invoke
candyBarVendor.dispenseItem(): a message should printed in the Terminal Window to say that you are coins to the value of 10 units short! - Invoke
candyBarVendor.insertCoin(50). - Invoke
candyBarVendor.dispenseItem()which should now succeed. - Invoke
candyBarVendor.getCurrentBalance(): result should be 40. - Invoke
candyBarVendor.refundCurrentBalance(): result should be 40. - Invoke
candyBarVendor.getCurrentBalance(): result should be 0. - Invoke
sodaVendor.insertCoin(1). - Invoke
sodaVendor.insertCoin(0): should print an error message. - Invoke
sodaVendor.insertCoin(-1): should print an error message. - Invoke
sodaVendor.getCurrentBalance(): should return 1.
Using the main method in BlueJ
You can also run the main method by invoking void main(String[] args) directly from the VendingMachine class and accepting the default input parameters VendingMachine({}). The results, which should match the text in results.txt, will appear in the Terminal Window.
| Download code |
