In this lab we shall build a very basic virtual ticket machine and in doing so gain further experience with some of the elements of classes such as fields, constructors and methods. We shall conduct a rudimentary analysis of the ticket machine class and implement some design improvements.
Unarchive chapter02.zip BlueJ sample files, available here, into the directory workspaceBlueJ/bluej. Then copy chapter02 into your working tree workspaceBlueJ/labs.
Open the project naive-ticket-machine.
Activate the BlueJ Terminal Record method calls functionality as shown in Figures 1, 2 and 3.
Create an object ticketMachine1 using 500 as an actual parameter (argument) as illustrated in Figure 4.
The argument 500 represents the cost of the ticket that this naive ticket machine issues.
Inspect the TicketMachine methods: right clicking on the TicketMachine object opens a drop-down menu containing all the methods in the class.
These are:
int getBalance()
int getPrice()
void insertMoney(int amount)
void printTicket()
Call getBalance. As expected you should have zero balance.
Call getPrice. The price of the ticket should be returned as 500, the figure entered as an argument.
Now print a ticket and inspect the BlueJ Terminal Window. This may be as illustrated in Figure 5.
The commands executed to instantiate TicketMachine and to print the ticket are shown on the ticket in Figure 2. If you wish not to see these commands click on the Terminal window and untick Record method calls in the BlueJ Options menu (Figure 6).
We have already revealed a serious design flaw in our ticket machine application which you no doubt have observed.
The flaw is that we have succeeded in printing a 500 cents ticket without inserting any money.
In fact we could print an indefinite number of such tickets.
Let's explore the behaviour of TicketMachine further.
Insert 100 cents and print a ticket.
Then verify that the balance is zero by invoking getBalance().
Now let's insert money in increments and verify the balance is correctly updated each time you make an addition.
Starting from zero balance insert in turn 100, 400, 100.
The final balance should be returned as 600 cents. So far so good.
Now with a balance of 600 print off a ticket. Remember the ticket costs 500 cent.
Check the balance. It returns zero. Clearly this is a no-refund machine.
Here is another design flaw: It is possible to insert negative amounts of money as an actual parameter into the constructor. For example:
TicketMachine ticketMachine = new TicketMachine(-500);
Here is a summary of the tested behaviours of the ticket machine:
We shall now set about correcting these design errors.
Still in naive-ticket-machine click on the BlueJ naive-ticket-machine window and using the menu commands Project | Save as save a copy of the project to your folder workspaceBlueJ/labs/session02.
Double click on the TicketMachine class graphic to reveal the underlying source code (Figure 1).
Here, below, is the complete class. It is good practice to comment code but we have removed the comments for the sake of compactness and to allow their introduction as each part of the class is explored and upgraded.
public class TicketMachine
{
private int price;
private int balance;
private int total;
public TicketMachine(int ticketCost)
{
price = ticketCost;
balance = 0;
total = 0;
}
public int getPrice()
{
return price;
}
public int getBalance()
{
return balance;
}
public void insertMoney(int amount)
{
balance = balance + amount;
}
public void printTicket()
{
System.out.println("##################");
System.out.println("# The BlueJ Line");
System.out.println("# Ticket");
System.out.println("# " + price + " cents.");
System.out.println("##################");
System.out.println();
total = total + balance;
balance = 0;
}
}
Refactor TicketMachine constructor
We will impose a pre-condtion on the constructor to ensure that negative numbers are not processed as arguments.
This is the constructor code we wish to refactor:
public TicketMachine(int ticketCost)
{
price = ticketCost;
balance = 0;
total = 0;
}
The statement price = ticketCost; :
The statements balance = 0; and total = 0; initialize these instance variables to zero.
To ensure that only positive ticketCost values are processed we could refactor the constructor as shown in Figure 1.
The statement
if (ticketCost > 0)
checks if the ticket cost exceeds zero and if it does, the statements within the following curly braces are executed, namely:
price = ticketCost;
balance = 0;
total = 0;
Should an attempt be made to enter zero or a negative ticket cost, this block of code immediately above would be skipped over and the block following the word else would be executed (which would print a warning).
The words if and else are Java reserved words and consequently may only be used in an appropriate context.
String if = "this is an illegal statement because if is a reserved word";
This refactoring simply alerts the user to the attempt to construct an object illegally. However the application still proceeds to construct a TicketMachine object in which the object state remains uninitialized.
This is far from a perfect solution but will suffice for now. We will address this issue again later but with an industry-standard solution.
Carry out this refactoring, recompile the code, run and test.
The test should include:
Refactor method: insertMoney .
Here is the existing code:
public void insertMoney(int amount)
{
balance = balance + amount;
}
Again, we wish to prevent insertion of a negative amount of money.
Here again is a temporary solution (Figure 1).
Carry out this refactoring, recompile the code, run and test.
Refactor printTicket method
We must ensure ticket printed only when an adequate balance present.
Here is the existing code to print a ticket:
public void printTicket()
{
System.out.println("##################");
System.out.println("# The BlueJ Line");
System.out.println("# Ticket");
System.out.println("# " + price + " cents.");
System.out.println("##################");
System.out.println();
total = total + balance;
balance = 0;
}
Three actions are performed in this method printTicket.
Here is the refactored code to update cumulative value of all tickets issued by this instance of the TicketMachine:
total = total + balance;
This statement does not perform the required task.
It should read
total = total + price;
When a ticket is printed the balance requires adjusting.
The statement
balance = 0;
clearly does not perform the required action, namely, updating the balance each time a ticket is issued.
Here is the refactored code to update the balance:
balance = balance - price;
We must also ensure that the balance is sufficient to pay for the ticket.
This refactoring can be achieved as follows:
public void printTicket()
{
if (balance >= price)
{
//print ticket
}
else
{
//warn user insufficient funds
}
}
The refactored method is illustrated in Figure 1.
Carry out this refactoring, recompile the code, run and test.
New method refundBalance
Once a ticket is issued the balance is refunded to the client.
This requires a new method which we shall call refundBalance.
The signature of the getter method (including modifier and return type) is:
public int refundBalance()
Here is what refundBalance does:
The complete class, including some documentation, is illustrated in the next step.
public class TicketMachine
{
/**
* Create a machine that issues tickets of the given price.
* Note that the price must be greater than zero.
*/
public TicketMachine(int ticketCost)
{
if (ticketCost > 0)
{
price = ticketCost;
balance = 0;
total = 0;
}
else
{
System.out.println("Please insert a positive ticket cost");
}
}
/**
* Return the price of a ticket.
*/
public int getPrice()
{
return price;
}
/**
* Return the amount of money already inserted for the
* next ticket.
*/
public int getBalance()
{
return balance;
}
/**
* Receive an amount of money in cents from a customer.
* A negative amount will be disallowed.
*/
public void insertMoney(int amount)
{
if (amount > 0)
{
balance = balance + amount;
}
else
{
System.out.println("Please insert a positive amount");
}
}
/**
* If sufficient funds available print a ticket.
* Update the total value tickets to date
* Update the remaining balance.
*/
public void printTicket()
{
// If the balance greater than ticket price then we can print a ticket
if (balance > price)
{
// Simulate the printing of a ticket.
System.out.println("##################");
System.out.println("# The BlueJ Line");
System.out.println("# Ticket");
System.out.println("# " + price + " cents.");
System.out.println("##################");
System.out.println();
// Update the total value of tickets to date.
total = total + price;
// Update the balance.
balance = balance - price;
}
else
{
int deficit = price - balance;
System.out.println("Please insert at least "+ deficit +" more cents");
}
}
}
Exercise 1
Exercise 2
Exercise 3
Exercise 4
Exercise 5
/**
* A class that maintains information on a book.
* This might form part of a larger application such
* as a library system, for instance.
*
* @author (TO DO: Insert your name here.)
* @version (TO DO: Insert today's date here.)
*/
class Book
{
// The fields
private String author;
private String title;
// TO DO: Add a field to store the ISBN
// TO DO: Add a field to store the cost of the book
/**
* Set the author and title fields when this object
* is constructed.
*/
public Book(String author, String title)
{
//TO DO: set the author and title fields to the contents of
// the passed parameters.
}
/**
* Set the author, title, ISBN and cost fields when this object
* is constructed.
*/
//TO DO: write a second constructor that has four parameters and use
// the data in these parameters to update the instance fields
// Make sure that the cost of the book is greater than zero.
// TO DO: Add a getter method for each instance field (i.e. you
// will write four methods here.
// TO DO: Add a setter method for each instance field (i.e. you
// will write four methods here.
// When writing the setter for the cost field, only update the cost fields
// if the value entered is greater than zero.
}
Exercise 6
Exercise 7
In this project, create a new class called Product.
This class should have four instance fields:
This class should have one constructor taking in values for each of the four instance fields.
This class should have accessor and mutator methods for each instance field.
Enter Javadoc comments for the class. Make sure to use @author and @version.
Enter Javadoc comments for each method. Make sure to use @param and @return, where appropriate.