CMSC 10200 Lab — Spring 2006
Lab Assignment 3

This lab is due 11:59pm on Sunday. There is Extra Credit available for this lab, described in the final section. Make sure you complete the lab before you start the extra credit.

Cleveland's Diner

Cleveland's Diner is a little greasy spoon squeezed into the corner of a high-rise on a busy thoroughfare. You've probably all eaten at a place like this. Cleveland's has a simple menu (only breakfast dishes). They need a system for taking orders and computing prices. In this lab you will turn the Diner menu into a collection of Java classes which represent the items offered at Cleveland's and collected together in a Java package.

The menu and prices (in dollars) at Cleveland's are as follows:

Drink Items
Item Price
Coffee
Tea
1.00
Coke
Diet Coke
1.25
Milk 1.50
Orange Juice
Grapefruit Juice
2.00

Side Items
Item Price
Grits 1.50
Hash browns 1.75
Fruit 2.25
Meat Item
Item price
Sausage
Bacon
Tofu
2.00

Egg Item
(Choice of Type and Number of Eggs)
Item price
Scambled
OverEasy
Poached
0.75 per egg
Pancake Item
(Choice of Type and Number of Cakes)
Item price
Plain
Buckwheat
1.00 per cake
Blueberry 1.25 per cake

There are also several plate choices:
  1. Egg Plate: Choice of Egg, Meat, and Side item. Also comes with a choice of White or Wheat toast. Price: sum of the prices of the Egg, Meat and Side items.
  2. Pancake Plate: Choice of Pancake, Meat and Side item. Price: sum of the prices of Pancake, Meat and Side items.
  3. Huevos Rancheros: Choice of Egg topped with spicy chili sauce and served with beans and rice. Choice of Corn or Flour tortillas. Price: Price of Egg dish + $3.00.

Getting Started

Read carefully the following start instructions.

  1. Create a directory lab3 wherever you want to work.
  2. You will need to set the compiler for Eclipse to version 5.0. See here.
  3. Create a new project called Diner.
  4. Create a text file called Readme and put your name in it. You may write any comments you want me to see in this file.
  5. Create a new package inside Diner and call it menu (lower case 'm', or Eclipse will complain!)
It is Java practice is to group all commonly related Java classes into a single package, which is standardly written in lower-case. The menu package will collect all classes you need to represent the Cleveland menu.

Implementing The Diner Menu

What is Expected

In this section you will create the classes in your Java menu for Cleveland's Diner. This menu will consist of several Java classes:

Each of these classes will implement a Java interface called Item
public interface Item {
	public int price();
}

Each class you write will implement the Item interface. Each class will be evaluated based on the following:

  1. A class constructor whose parameters are determined by the data fields of the class.
  2. A price method for computing the price of the object in pennies.
  3. A toString method for providing an informative and intelligible description of the object.
  4. A main method which provides a thorough range of test cases. This is described in Testing.
  5. Documentation of your public methods through public comments. This is explained in Commenting.
  6. Appropriate handling of inappropriate arguments to the constructor method. In this lab we will handle exceptions by convention, which is described further below. For Extra Credit you can handle inappropriate arguments by Java's Exception class.
  7. Code Style. Your data fields need to be of an appropriate type. Your code should be clear and concise. These are not unrelated. This is further described in Style.
Here is an example of how you implement the Drink class
public class Drink implements Item {
	
	public enum Type{
		COFFEE, TEA, MILK, ORANGE, GRAPEFRUIT, COKE, DIET
	}
	// private data field
	private Type type;

	/**
	 *  Expects non-<code>null</code> reference. 
	 *  
	 **/
	public Drink(Type type) { this.type = type; }
	
	/**
	 * Compute the price of Drink
	 *
	 * @return whole number of pennies
	 */
    	public int price() {
		// TODO Auto-generated method stub
	}
	
	/**
	 * Produces String description of Drink.
	 * Examples:  
	 * <ul>
	 *   <li>coffee:  "COFFEE"
	 *   <li> coke:  "COKE"
	 * </ul>
	 *
	 * @return String description of Drink object
	 */
        public String toString() {
		// You must fill-in body
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
	}
}
You are forced to write a price() method because Drink implements Item. Java does not force you to write toString() because every class is a subclass of the Object class which has already implemented this method. I included toString in Item to make you aware that you will need to produce an informative description for any object which is an Item. You will also need to write a constructor which initializes your private data field and a main method where you test your new class. Every class follows this form.

Creating Interface and Classes in Eclipse

To create a new interface Item inside the menu pacakage:

Create a new interface inside the menu package. (You can write-click on menu, or <ctrl> + mouse-click on the Mac. This gives you a menu, which you can choose Interface and call it Item.
Eclipse will give you a wrapper
	package menu;

	public interface Item {

	}
And you can fill-in the body of the interface with the methods price. The first line package menu; tells the compiler that Item is part of the package menu. Any class in this package may refer to any other class in the package by name. To construct a new Drink inside the package:
    Drink coke = new Drink(Drink.Type.COKE);
But outside the package, Java requires you include the package name before the class: menu.Drink. This week all your coding will be inside the menu package, next week you'll see how to use your new package.

It is easy to create new classes in the menu package which implement the Item interface and have a main method. Try the following steps for the Drink class:

  1. Open a new class in the menu package.
  2. In the New Class Wizard, select add next to Interfaces, then type in the interface Item.
  3. Make sure you select the main method stub in the New Class Wizard.
  4. You are ready to add code.
You will notice that Eclipse has given you several things:
  1. package menu; at the top of the file.
  2. A public class wrapper which implements Item.
  3. A method stub for the print method. This was forced because you are implementing the Item interface.
  4. A method stub for the main method, which Eclipse nicely gave you because you requested it.
  5. You must add data fields, a constructor and the toString method, which will overwrite the toString method defined by the Object class.

Commenting Your Code

There are two sorts of comments in Java:

Private comments are intended for those who are reading your code, and Public comments are intended for those who are using your code. You have already seen public comments used in class. What makes them so special is that there is a special utility called Javadoc which formats public comments and creates a nice webpage which documents how your code is to be used. We will see more of this in later labs, but for now you can copy and modify my examples above. Here are some points to keep in mind Here is a Tutorial on Javadoc. For now, you can modify my sample comments in the Drink class.

Handling Bad Arguments by Convention

Suppose a user of your Drink class tries to create an object by passing your constructor a null reference? It is impossible for your constructor to produce an object. Java provides a mechanism for handling these kind of problems with the Exception class. As Extra Credit you may handle bad arguments by using Exceptions. You should only attempt this after you finish writing your class code. For now, we will handle bad arguments by convention. This means that you will document as part of your class the range of acceptable arguments (or what arguments are unacceptable), and guarantee your methods will work for these arguments. You document this in your public comment before constructors. The user agrees by using your code, by contract, that only acceptable arguments will be passed. The user accepts responsibility fo all the awful consequences of passing unacceptable arguments.

Testing Your Code

All code you write must be thoroughly tested using a test driver (a main method) inside each class. You must prove to me your code works correctly, by showing that it correctly works on any possible use of your class. This means you will have to carefully consider how your class could be used. You may assume that all constructors are called with correct parameters, so you can create intelligible objects. When you do the Extra Credit you can handle the exceptional cases where your constructor is called with incorrect arguments. Test each class before you write the code for the next class.

The Drink class has only seven possible objects which could be created, so it is reasonable to test each instance. Here is an example of how to write your test driver to test two of the seven possibilities:

public static void main(String[] args) {
   // Expected output:
   //	COKE	125
   Drink coke = new Drink(Drink.Type.COKE);
   System.out.println(coke + "\t" + coke.price());

   //Expected output:
   //   COFFEE	100
   Drink coffee = new Drink(Drink.Type.COFFEE);
   System.out.println(coffee + "\t" + coffee.price());

   // You write 5 remaining cases
}

Sometimes a class has too many instances to realistically test all. For example, the Pancake class allows a choice of the number of Pancakes as well as the type. In this case you only need to test a few of the possible integers for each of the types. Remember, you may assume the number is intelligible, which means one or more, until you tackle the exceptional cases. Another example is the PancakePlate class which includes several other classes among its data objects. Since you have already tested each of these classes, you will only need a few instances of the PancakePlate class to show your code is correct.

Coding with Style

It is very important that you try to make your code brief and clear. Code style is very important, since code which is obscure and hard to read, often reflects confusion in the programmer and is likely to be buggy. I have links to Style, and you should take the time to implement the good practices recommended into your code writing.

Read this paragraph carefully. There are only a few types of drinks possible at Cleveland's, so using an enum class is appropriate. It has the value that the only possible bad argument to the constructor is the null reference. On the other hand, the number of eggs a customer would like is to be kept open ended. Why force a customer to order three eggs at a time? William "The Refrigerator" Perry claimed to eat 27 eggs (scrambled) at one sitting!! In this case an integer is appropriate to store the number of eggs. By implementing the Drink type as an enum, you have the added bonus of being able to use the switch statement to make your code clearer:

switch (type) { // type is a Drink.Type
  case COFFEE: case TEA:
    // do stuff
    break;
  case COKE:  case DIET:
    // do stuff
    break;
  case MILK:
    // do stuff
    break;
  case ORANGE:  case GRAPEFRUIT:
    //do stuff
    break;
  default:
    // This case can only arise if type is a null reference
}

Handling Exceptions

Handling exceptions only requires adding a few lines of code to each class, and adding another test case or two to your test driver. Make sure your classes work in the expected cases before you attempt to handle the exceptional cases. You should read Chapter 9 (Error Handling with Exceptions) of Thinking in Java by Bruce Eckel, for a more detailed description of exceptions in Java.

What should your Drink constructor do if the user passes a null reference for a Drink.Type? You could ignore it, but you would not be creating an intelligible object. Java provides a special Exception class, NullPointerException for just this possibility. Here is how you can rewrite your constructor for the Drink class:

/**
 *
 * @exception NullPointerException a null reference
 */
public Drink(Type t) {
    if (t == null)
       throw new NullPointerException();
    else
        this.type = t;
}
If the user passes null to this constructor, no Drink object will be created, but an exception is raised, which can be used to signal the method which attempted to create a Drink object that no object was created. If t == null, then Java creates a NullPointerException object and throws this object--which means the constructor terminates on the spot.

In addition to including the new code, you need to document that your constructor throws an exception. This is placed in a public comment using @exception:

@exception [Exception thrown] [Exception description]

In order to recieve the thrown exception, a method must inform the compiler that it wishes to catch the signal. Here is an example of how this could be done in your test driver:

// Expected output:
//   COKE	125
// Unless a NullPointerException is raised.
Drink.type t;  // t == null
try {  // try block
   Drink coke = new Drink(t);
   System.out.println(coke + "\t" + coke.price());
} catch ( NullPointerException e) {  // catch block
   System.err.println("Whoops!! Passed a null type.");
}
The try block is where you try to create a Drink object, but you want to catch an exception if this attempt fails. The catch block does two things:
  1. It says you want to catch a NullPointerException, and creates a reference for a NullPointerException object called e. This object is typically not used, since usually all you need to know is what exception was raised.
  2. It says what you will do if a NullPointerException is raised. In this case, print a message.
You also see a new method call
System.err.println(String s)
Normally, output is written to Standard output, or System.out in Java. This is typically the terminal (or console in Eclipse), but it may be a file. The user expects that error messages (which are not expected as part of the output) to be sent to Standard error, or System.err in Java.

How do you handle exceptional arguments to your constructor in a class like the Pancake class? There are two sorts of problems here:

  1. Passing a null Pancake type, for which you raise a NullPointerException.
  2. Requesting less than one pancake. (You cannot have a negative number of pancakes, and requesting zero pancakes is stupid, and probably a mistake.) Java provides an exception called IllegalArgumentException (remember IAE from class) for this purpose.
To handle more than one possible exception you provide several catch blocks, one for each exception:
Pancake.Type t = Pancake.Type.PLAIN; 
int n = -1;  // stupid value
try {  // try block
   Pancake p = new Pancake(t, n);
   System.out.println(p + "\t" + coke.price());
} catch ( NullPointerException e) {  // catch block
   System.err.println("Whoops!! Passed a null type.");
} catch ( IllegalArgumentException e) { // catch block
   System.err.println("Whoops!! Passed a bad number.");
}

Handin

Your lab is due Sunday at 11:59pm. You will submit your Diner project folder. You must be inside this directory to submit. See handin for details


Kenneth Harris