Building a Class (money)
CS 162 - Spring 2008

Goals
- to learn about the parts of a Java class
- to enhance a simple Java class

Overview
This hands-on activity will guide you through enhancing a simple Java class. To keep the exercise an appropriate size, this version lacks many methods.

The CoinPurse class is an abstract data type representing a coin purse containing quarters, dimes, nickels, and pennies. The class maintains how many of each coin denomination the coin purse contains. The actions are:

CoinPurse - no-parameter constructor
builds an empty coin purse
CoinPurse(int q, int d, int n, int p) - constructor
takes in the number of each coin denomination and builds a coin purse containing them;
the precondition is that input values are not negative
toString
returns a string representation of the coin purse
value
returns the value of the coins in the purse as cents
numQuarters
returns how many quarters are in the coin purse
addQuarters(int q)
adds the provided number of quarters to the coin purse only if that number is not negative
transferFrom(CoinPurse other)
transfers the entire contents of the other coin purse to this one leaving the other one empty
The class throws an IllegalStateException if the pre-conditions for methods are not met. It may have abnormal results if arithmetic overflow occurs.

There are various ways to represent the CoinPurse object. We have chosen to use four integer instance variables. The class invariant for a class is a precise description of how the instance variables represent the object. It states what is true for a correctly formed instance of the class. Each method can depend on that invariant being true when it starts and must ensure that it is true when it finishes.

For the CoinPurse class, the class invariant is:
- instance variables are quarters, dimes, nickels, pennies
- each instance variable represents how many of that denomination coin are in the CoinPurse
- each instance variable must be non-negative

Preparing for the Lab Session
Before you come to the lab session, read through this handout and the code so that you are familiar with what you'll be doing in the lab session.

Copy the money directory from your instructor's shared file space. There are three files there: CoinPurse.java, PlayMoney.java, and PlayMoneyComplete.txt. The first is a starting version of the CoinPurse class. The second and third are "driver", "client", or "demonstration" classes. They have a main program that uses the CoinPurse class. You'll start using and modifying the PlayMoney class but will copy the contents of the PlayMoneyComplete file at the end of the activity.

Examine the code for the CoinPurse class and identify these items: the instance variables, the class invariant, the no-parameter constructor, the parameterized constructor, the toString method, and where you will place your name. Notice that the instance variables are private but that the methods are public.

Examine the code for PlayMoney and identify these items: objects built with the no-parameter constructor, objects built with the parameterized constructor, and the value in cents for each of the CoinPurses. Calculate those values and write them down.

Steps in the Lab Activity

0: Create a new project using the provided files and briefly play with it.
Create a new project and run it. Change one of the linen constructor values to a negative value. Save and run that. Before moving to the next step, be sure that what you have is executing without throwing exceptions.

1: Add a public method to compute the value of the CoinPurse.
In CoinPurse, create a public method to return the value of the purse. The code is:
   // returns the total value of the CoinPurse
   public int value( ) {
      return (quarters*25) + (dimes*10) + (nickels*5) + pennies;
   }
Note that you do not pass the instance variables as parameters. They are implicit parameters available in any of the instance methods of the class. This is an accessor method that does not change the state of the object. Therefore, it does not affect the class invariant.

2: Adjust PlayMoney to use the value method.
Update the printPurse method in PlayMoney to print the total value. Use  cp.value( )  to call the value method of the cp object. That will return the value of the cp object. The output for the vinyl purse should look like:
Vinyl purse contains 2 quarters, 1 dimes, 3 nickels, and 4 pennies which total 79 cents.

3: Add the addQuarters mutator/modification method.
The addQuarters method is a modification or mutator method with a void return type. Like the other methods in this class, it is public. It takes in a single integer parameter which must be non-negative. If that parameter is negative, throw a new IllegalStateException (see the constructor for how to do that). Otherwise, add the value of the parameter to the value in the quarters instance variable.

Ask yourself whether you kept the class invariant true. Did your change make it false? No, by checking that the parameter was non-negative, your code didn't allow the quarters value to become negative. However, it could overflow. You could throw an IllegalStateException if this condition is true: q > (Integer.MAX_VALUE - quarters) However, a lot of software is written ignoring the possibility of integer overflow because in normal usage, it won't happen. For this assignment and most assignments in this course, ignore the integer overflow. Don't check for it.

4: Add the numQuarters accessor method.
The numQuarters method is an accessor method that returns the number of quarters in the coin purse. It returns the number of them, not their value, as its integer return value.

5: Adjust PlayMoney to add and report on quarters.
Modify PlayMoney to add 3 quarters to the leather coin purse and then print how many quarters are in the leather coin purse. Run that. Make a change to trigger the exception and see that it happens. Adjust the code back to not causing an exception.

6: Add the transferFrom method.
The transferFrom method will transfer the contents of another CoinPurse to this one. All the coins in the other one will be added to this one and will be removed from the other one. But we have to avoid self-reference. That is, we can't transfer from the vinyl purse to the vinyl purse.

We need to check for self-reference by ensuring this coin purse is not the same object as the other coin purse. Use the inequality operator for that. This code works for the beginning of the transferForm method:

	// as long as the other CoinPurse is not this one,
	// transfer all of its coins to this one and make it empty
	public void transferFrom(CoinPurse other) {
		if (this != other) {
			quarters += other.quarters;
Note that you can refer to this object with the special keyword this. Refer to the quarters instance variable of the other coin purse with other.quarters. You could, but usually don't, refer to this object's quarters instance variable with this.quarters. Add the values of all the instance variables from other to this. Then set the instance variables in other to zero.

Did you maintain the class invariant for both coin purses? Could you prove that?

7: Update PlayMoney to use transferFrom.
In PlayMoney, you could create some code to transfer the contents of one coin purse to another and print out the results. We've done that for you. Open PlayMoneyComplete.txt, select all, copy, and paste it into PlayMoney. At the end of execution, does the plastic coin purse contain ten of each denomination and the squeeze coin purse none? If so, submit your solution. Otherwise, modify it until the answers are correct.

8: Consider changes.
There are many ways to enhance this program. You could catch the exceptions in the driver. You could read values from the user and build coin purses with those values. You could implement the add and accessor methods for each of the coin denominations.

In developing this activity, we discussed having an extract method that would subtract and return a desired amount from a coin purse in a newly created CoinPurse. On the surface, this would be a change-making program, but you don't have an unlimited number of coins in each denomination. It's harder than it first appears.

For now, we'll leave this class as is.

CoinPurse.java

     1	// CoinPurse represents a collection of US coins
     2	// including quarters, dimes, nickels, and pennies 
     3	
     4	// Beth Katz and _______________ - January 2008
     5	
     6	public class CoinPurse {
     7	    // class invariant
     8	    // - instance variables are quarters, dimes, nickels, pennies
     9	    // - each instance variable represents how many of 
    10	    //       that denomination coin are in the CoinPurse
    11	    // - each instance variable must be non-negative
    12	    int quarters, dimes, nickels, pennies;    
    13	    
    14	    // constructs an empty CoinPurse
    15	    CoinPurse( ) {
    16	        quarters = 0;
    17	        dimes = 0;
    18	        nickels = 0;
    19	        pennies = 0;        
    20	    }
    21	    
    22	    // constructs a CoinPurse from four values
    23	    CoinPurse (int q, int d, int n, int p) {
    24	        if (q < 0 || d < 0 || n < 0 || p < 0) {
    25	            throw new IllegalStateException("Illegal negative value:"
    26	                    + q + " " +  d + " " + n + " " + p);
    27	        }
    28	        quarters = q;
    29	        dimes = d;
    30	        nickels = n;
    31	        pennies = p;
    32	    }
    33	
    34	    // returns text representation of a CoinPurse
    35	    public String toString( ) {
    36	        return (quarters + " quarters, "
    37	                + dimes + " dimes, "
    38	                + nickels + " nickels, and "
    39	                + pennies + " pennies");
    40	    }
    41	}

PlayMoney.java

     1	// Uses the CoinPurse class
     2	// Beth Katz - January 2008
     3	
     4	public class PlayMoney {
     5	
     6	    public static void main(String[] args) {
     7	        CoinPurse leather, vinyl, linen;
     8	        
     9	        leather = new CoinPurse( );
    10	        printPurse(leather, "Leather");
    11	        
    12	        vinyl = new CoinPurse(2, 1, 3, 4);
    13	        printPurse(vinyl, "Vinyl");
    14	        
    15	        linen = new CoinPurse(1, 0, 0, 0);
    16	        printPurse(linen, "Linen");
    17	    }
    18	
    19	    // prints a purse with a label
    20	    public static void printPurse(CoinPurse cp, String label) {
    21	        System.out.println(label + " purse contains " + cp);        
    22	    }
    23	
    24	}