Program 2

Writing Classes + Arrays

Introduction to Computer Science II
ICS-22


Introduction This programming assignment is designed to ensure that you know how to write classes (including the use of arrays and interfaces). Primarily, you will be writing methods, but you will also be defining fields (mostly instance variables) and writing constructors. Finally, you'll begin to practice writing, testing, and debugging classes using iterative-enhancement. Do not use inheritance on any part of this assignment, nor should you use ArrayList or LinkedList (use "raw" arrays in the third part, where you need to store a sequence of values).

To write a class using iterative enhancement, we use the following process.

  1. Write a driver for testing the class, typically a console program that allows us to call every method in the class. I will often provide these; later we will examine writing a different kind of driver using JUnit.
  2. Decide on the needed instance variables for the class and declare (and if possible initialize) them. You might later discover that you need more; if so, add them. Try to declare the minimum amount of state.
  3. Write the required constructors, initializing those instance variables whose values depend on arguments to the constructor.
  4. Write the header of each method, filled in with a minimal body. This is called stubbing methods in a class.
    • for a void method, the body can be just {}
    • for a method returning a reference type, the body should be {return null;}
    • for a method returning a primitive type, the body should just return some literal of that type (e.g., {return false;} or {return 0;} etc.)
  5. Once this class and its driver compiles, fill in the body of each method, then test it in the driver, then continue by filling in the body of the next method, etc. There is a bit of an art to determine which methods to implement/test first. A good heuristic is to implement/test toString first (to help debug the others), then implement/test the simplest methods first -the ones that do not call other methods defined in the class. Often (in the case of collection classes) the method for adding a value to a collection will have to be written early; later you can write/test the accessors and the method(s) to remove a value.

You will write three classes in this assignment; I will provide driver programs for testing each (you should study these drivers to ensure that you understand the code). As always, you can check the behavior of your programs against mine by downloading and running my Executables, to help you understand the specification of the problem and observe the programmer/user interaction you are to implement Continue to use good programming style when coding these classes. Also, read items 15-24 and 27-31 in Vermeulen, The Elements of Java Style (which appear on pages 18-25 and 26-29). For the SimplePriorityQueue class, you must write its Javadoc.

Instead of starting with empty project folders, download and unzip the Start Project Folders which contain the infrastructure that you will use to test the classes that you write in this assignment. These projects show compilation errors: you might need to rename or add some class files to these projects (certainly you will have to write some code, initially by stubbing methods); check that you have the necessary imports too. Write each program in its own project folder; they are named bigrationaldemo, vendingmachine, and simplepriorityqueuedemo; when you are finished with each, zip it and submit it via Checkmate. Each pair should submit one project: either partner can submit it. Of course, both partners names should appear in the comments of each program.


BigRational: Infinite Precision Rationals This assignment generalizes the ComputeE program, in the context of the Rational class. Write a class named BigRational in a package named by your UCI user id (if you are pat@uci.edu use the package name edu.uci.pat.ics22). This assignment is actually a bridge between using/writing classes, because you are actually translating the Rational class (the BigRational contains exactly the same the methods). But, instead of using two int instance variables for storing the numerator and denominator of a fraction, use two BigInteger instance variables, by importing and using the java.math.BigInteger class. In BigRational, these instance variables can store integers with any number of digits (tens, hundreds, thousands, etc). Refer to the Javadoc of BigInteger throughout this assignment (you might even want to print a copy).

What you must do is translate the Rational class so that instead of using ints it becomes the BigRational class and uses BigInteger. Operators applied to int variables must be translated into method calls on BigInteger variables. Most operators on ints are available as equivalent methods on BigIntegers. Translate the Rational.java file (change its name to BigRational.java) which contains all the Rational constructors and methods: translate each of these. The project file also contains a driver, which compiles when the the BigRational class compiles.

Here are some general hints for translation.

  • Wherever you see Rational and int in the class, change them to BigRational and BigInteger (except for the return type of the compareTo method, which should remain int, and the parameter to toDecimalString which should remain int).
  • The BigInteger class has static fields ZERO, ONE; and TEN; if you need any other BigInteger values (say, 23) use a constructor to create one (say, new BigInteger("23")). Note, if you have int i and want the equivalent BigInteger, you can use a constructor with a short idiom for converting int to String: new BigInteger(""+i)).
  • If int a,b,c; we write a + b * c but with BigInteger a,b,c; we write a.add( b.multiply(c) ); we can also write this expression as the cascaded method call b.multiply(c).add(a), although this looks a bit strange because the order of the operands has changed (e.g., c*c+a).
  • You have seen that there is a gcd method in the BigInteger class (so you should use this method directly in the constructor). DO NOT define a private static method for this operation in the BigRational class, although you can examine this method in the Rational class to see how Euclid's GCD algorithm works: this is one of the oldest known non-trivial algorithms.
  • When writing the compareTo method for BigRational make use of the the compareTo method for BigInteger (and remember, it should still return an int).
  • Finally, I have already completely translated the toDecimalString method, which appears in both the original (commented out) and updated form. Do not change any of this code, but study it and use it as an example of how to translate the other methods.
Run my executable for further information. After you program compiles and runs, intially test its methods by using the driver and entering small values that you can compute the result for. Eventually try running the harmonic sum option with 100 or 200 for n and comparing its output to my executable. You do not need to write/update the Javadoc for this class.

Vending Machine Write a class named Model for the vendingMachine package. This class implements the actions of a vending machine which allows coins to be deposited and products to be bought (or transactions to be cancelled). It consists of some fields (you will have to infer most of these; plan on adding/removing fields as you implement/debug this class), methods, and a constructor with no parameters. It must implement the following public methods (see their headers below), which are called by the Controller and View classes to simulate a vending machine. The program will not compile without these methods; you should think about writing other private helper methods for this class as well, to avoid duplicating much code.
  • void cancel() is called when the cancel button is pressed. It should terminate any pending sale return whatever coins the user has deposited (not just an equivalent amount, but the same denominations of coins the user deposited). When the view is updated, it should explain this action in the message window at the bottom.
  • void deposit(int amount) is called when any of the money buttons are pressed (its parameter indicates how much money was deposited: it is always 5, 10, or 25 cents). When the view is updated, it should explain this action in the message window at the bottom.
  • void buy (String product) is called when any of the buy buttons are pressed (its parameter indicates which product is bought: it is either "Pepsi" or "Coke"). When the view is updated, it should explain this action in the message window at the bottom, including how much change is returned to complete the sale. Note that when an item runs out, the button to buy that item becomes grey (in the view) un-pressable (in the controller).
  • There are six accessors, each of which the view calls when it needs to display information about the state of the vending machine (for example, to determine whether or not a buy button is pressable).
    • String getDeposited() returns the total amount of money deposited since the transaction started (as a String).
    • String getMessage() returns the message to display (at the bottom, in blue).
    • int getCokeLeft() returns the number of coke bottles in the machine.
    • int getPepsiLeft() returns the number of pepsi bottles in the machine.
    • String getCokePrice() returns the price of a coke.
    • String getPepsiPrice() returns the price of a pepsi.
Run my executable for further information. Deposit coins, make purchases (or cancel the purchases; try to make purchases with too little money) and observe the information that the view displays; it should work according to your expectations of how a vending machine should work. Pay particularly close attention to...
  • ..what happens when you cancel a purchase; you get back exactly the coins you deposited (e.g., if you put in 3 dimes, you get three dimes back, not a quarter and a nickel). Note that the result must read correctly: e.g., 1 dime but 2 dimes.
  • ..what happens when there is not enough (or not the right kind of) change left to make a purchase (the purchase should be disallowed).
  • ..what happens when there is enough change to make a purchase. It should return the fewest number of coins possible. Be careful, though: the change might be 25 cents, but it could be returned as 1 dime and 3 nickels because there are no quarters and only one dime.
For debugging purposes, each pressed button prints some information in the console window. Notice that the information returned from the toString method in the Model class is displayed in the console window too, for debugging purposes.

Remember to call view.update() whenever the state of the model has changed; this tells the view to recollect all the information that it needs to redisplay itself: in this case it will call all the tiny accessors to determine what to display.

Remember that the constructor for the model should prompt the user to enter the starting number of quarters, dimes, nickels, coke bottles, pepsi bottles, coke cost, and pepsi cost. In this way, you can more easily test some of the boundary cases discussed above. If you declare any other instance variables, initialize them appropriately without prompting.

You do not need to examine/alter code in the other classes in this package; the errors there will vanish when you add the right methods to the Model class. You do not need to write/update the Javadoc for this class.

Finally, if you want to test the Model class even without the View and Controller, I have added a main method to this class, which acts as a Text Driver for the class (see the associated .bat file too).


A Priority Queue Class Write a class named SimplePriorityQueue in a package named by your UCI user id (if you are pat@uci.edu use the package name edu.uic.ics22). We have already discussed and examined the SimpleStack and SimpleQueue collection classes. A priority queue is similar to a queue in that values can be enqueued into it and dequeued from it (and all the same accessors are allowed). The major difference is that values are dequeued in order of their priority (highest priority first), not in the order that they were enqueued. Think of a line in Hollywood, where stars get to leave the line and be served first, no matter when they entered the line. Many of the methods can be written independently of the enqueue/dequeue behavior: makeEmpty, isEmpty, getSize, and toString.

There are very many different interesting ways to implement a priority queue, some using advanced data structures (heaps, binomial trees, etc). In this assignment you and your partner will implement a simple (and slow) one, using a 1-d array. Some partners will do it one way; some partners will do it another way. You and your partner are to implement the methods depending on the last name of you and your partner, using whoever's last name comes first in the alphabet. In the Pattis/Stehlik programming partners, the implementation is done according to Pattis. You are to implement your class using one of the two following ways.

If the last name starts with A-J, write the class so that the values in the priority queue appear in the array in no special order. The enqueue method should put its value at the rear of the array (doubling its length if necessary). The dequeue method should scan this array to find the value with the highest priority, remove it (by moving the current rear value to its position -after all, there is no order in the array), and return it. The peek methods works the same, but without removing the value.

If the last name starts with K-Z, write the class so that the values in the priority queue appear in the array in a special order: sorted from lowest priority (at index 0) to highest priority (at the highest index). The enqueue method should put its value in the correct position in the array (doubling its length if necessary). The dequeue method should remove the value at the highest index (it has the highest priority) and return it. The peek methods works the same, but without removing the value.

Some ground rules: You may not write/call any sorting method in your code. Write private helper methods to avoid duplicating code inside any methods (in one implementation, there is lots of code in dequeue and peek that is similar). When you remove any references from the array, ensure that you store null there: don't leave in a reference to any old values, or duplicate references to any values.

The only loose end is how to determine which value has the highest priority (needed either in the methods that enqueue or dequeue/peek). We want SimplePriorityQueue's to acquire this prioritizing information, not have any special ordering built into them. Also, we will not assign priorities to values, but just answer the question of whether one value has a higher priority than another. This is a perfect place to use the Comparator interface.

When we construct a SimplePriorityQueue we will supply it with an object constructed from a class implementing Comparator. Then, we can use the compare method of this object to determine the priority ordering of two values whenever we need to.

You will be required to write (and add to the project) a variety of classes that implement this interface (all in a package using your standard name, e.g., edu.uci.pat.ics22). In the first four implementations, you can assume Strings are being compared. This is for use in the general driver.

  1. StringByLength: the highest priority value is the shortest string.
  2. StringByLengthReverse: the highest priority value is the longest string.
  3. StringByValue: the highest priority value is the string with the smallest value (according to how values would be ordered in a dictionary).
  4. StringByValueIgnoreCase: the highest priority value is the string with the smallest value (again, via dictionary ordering), but this time ignoring case.
In the last one, you can assume Integers are being compared. This is for use in a special part of the driver.
  1. IntegerIncreasing: the highest priority value is the smallest integer.
The driver has an i option that constructs a SimplePriorityQueue which is prioritized by IntegerIncreasing. When we choose this option, the driver automatically enqueues a bunch of integers and dequeues them all (they should come out in increasing order: so, priority queues can be used for sorting). Th IntegerIncreasing class is written similarly to the four classes prioritizing Strings, but instead casting to Integer. When you create all these small classes in Eclipse, you will put each of them in a separate file. You can leave the StringByLength class, illustrated below, in my package. Put each of the new classes in your own package. Define them similarly; check out the Javadoc for String and Integer to find methods that make all these classes simple to write.
  public class StringByLength implements Comparator {

    public int compare(Object o1, Object o2)
    {
      String s1 = (String)o1;
      String s2 = (String)o2;
      return s2.length() - s1.length();  //Note the order of s2 and s1!
    }

    public boolean equals(Object o1)
    {return (o1 instanceof StringByLength);}
  }
Note that compare("ab","xyz") returns a positive value (3-2 = 1) indicating that the first argument has a higher priority than the second (smaller length). Another way to implement this is return -(s1.length()-s2.length()); meaning that the priority is the opposite of the standard ordering: if the first is less than the second, it has higher priority.

Examine the Application.java file. When it constructs a SimplePriorityQueue object, it passes it an object constructed from some class that implements the Comparator interface (i.e., an object constructed from one of the classes above). Run my executable to better understand the meanings of these Comparator classes; enqueue and dequeue lots of different values.

The SimplePriorityQueue class should define two constructors. The first takes an object from a class implementing Comparator and an and initial size for the array (which must be positive, otherwise throw an exception); the second just takes such an object (and by default uses an array of size 1). Hint: I used just three instance variables: one for the array; one for the amount of that array actually used, and one for the Comparator object.

Finally, modify the SimpleQueue provided in the start folder that you download (it is an excellent starting point because queues and priority queues have the same method names -and some even have the same semantics). Update its Javadoc to be correct for the SimplePriorityQueue class.

To generate the Javadoc for this class, select Project | Generate Javadoc... and then make sure the box for the entire project in the Select types for which Javadoc will be generated is fully checked (if you click the disclosure box, make sure both the packages -default and the one with your id- are checked. Examine the other options, but don't change them. Note that you can genrate Javadoc for users (showing only public information) and Javadoc for yourself (showing members with other access modifiers: just go with public).

For Destination browse to the folder containing this project. For Javadoc command configure Eclipse to refer to the javadoc.exe executable file in the bin folder where the jdk is stored. Finally click Finish; my Generate Javadoc window looked like this. .

The console window will show a trace of your Javadoc being generated. To examine/read the Javadoc, look in your project folder under Doc and double click the index.htm file. Submit the Javadoc with your project (don't remove it before zipping). We will look at it and ensure that you have updated it from queue to priority queue.


Extra Credit There is no extra credit for this assignment (beyond turning in your solutions early).