Processing & Java: An Introduction to Computing

Lab 9: Classes

Students who complete this lab will demonstrate that they can:

- Design and implement classes, including:
- constructors
- accessors
- mutators
- other utility methods

- Implement object interaction methods

In this lab, you’ll work with basic class design and implementation in Java.

- Create an appropriate package for lab 9.
- Create a new class named
`Fraction`

. This class will not be a Java application, so it does not need a`main()`

method.

`Fraction.java`

and declared a new class in this file with the same name,
`Fraction`

. You will fill in the elements of this class definition iteratively and incrementally in this lab.
The state of an object is represented by the values of its attributes. For example an object representing the fraction
one-half will have a numerator of 1 and a denominator of 2. These attributes will be implemented as private, *instance
variables* (not public or static) and will be in scope throughout the entire class definition.

At this point, it’s important to think about valid and invalid object states. Because division by zero is
undefined, a fraction object must have a denominator that is not zero. This condition must always be true and is thus
called an *invariant*.

Your faction class will need to keep track of the fraction’s numerator and denominator. Add instance variables
to your
`Fraction`

class to represent these values. Make them integers and use the naming convention in the text by naming them
`myNumerator`

and
`myDenominator`

.

Add documentation for your class, including a one-line description of what we are modeling. Use a boolean expression to specify the class invariant with respect to the values of the instance variables, and be sure to include your name and the date as usual.

This class definition should have no compiler errors, but at this point it will not actually run.

The behavior of class objects is specified using methods, including output methods, constructors, accessors, mutators and other utility methods. The remaining exercises address these methods one at a time.

`toString()`

Method
When Java prints out an object, it automatically calls the
`toString()`

method defined for that object. A new class can override the default implementation of this function to specify
exactly how it would like its object to be printed.

Implement a
`toString()`

method that receives nothing from the calling program and returns a string representing the current state of the
fraction object. For example, if the fraction is representing one-half (i.e.,
`myNumerator`

is 1 and
`myDenominator`

is 2), then return “1/2”.

Again, this new method definition should have no compiler errors but will not at this point actually run.

We construct class objects using *constructor* methods. Constructors are very much like normal functions except
that they:

- are specified as public
- have no return type
- have the same name as the class

The simplest of the constructors is the default constructor. Default constructors receive nothing from the caller and initialize the instance variables to valid, default values.

Build a default constructor for your fraction class. Implement the following algorithm:

**Assumptions**:`myNumerator`

and`myDenominator`

have been declared as integer instance variables.**Algorithm**(default constructor)**Set**`myNumerator`

= 0.**Set**`myDenominator`

= 1.

These values are reasonable defaults and they do not violate the fraction class invariant. Add documentation for this method explaining what it does.

Now that you have a class, constructor and output method, create a new
`FractionConsole`

application class (in the same package as your
`Fraction`

class) to test your class. Use the following program:

public class FractionConsole { public static void main(String[] args) { Fraction f1 = new Fraction(); // Construct a default fraction object. System.out.println(f1); // Print the object. } }

This program should output
`0/1`

. Add appropriate documentation to this console program, and save the program so that you can submit it later.

It is likely that programmers will want to create their own fraction objects and to set numerator and denominator themselves. For this, we would like a constructor that accepts initial values as parameters.

Build the explicit-value constructor for your fraction class. Implement the following (slightly more involved) algorithm:

**Assumptions**:`myNumerator`

and`myDenominator`

have been declared as integer instance variables.**Algorithm**(explicit-value constructor)**Receive**integer values for`numerator`

and`denominator`

.**If**the values of`numerator`

and`denominator`

satisfy the invariant,**then**:**Set**`myNumerator`

=`numerator`

.**Set**`myDenominator`

=`denominator`

.

**Otherwise:****Print**an error message to`System.err`

.**Exit**the program.

Add documentation to this method explaining what it does.

To test the behavior of this new constructor, add the following code to your console application.

Fraction f2 = new Fraction(1, 2); // Construct a fraction object for one half. System.out.println(f2); // Print the object.

This new code segment should output
`1/2`

. Plan and implement a few additional test cases to be sure that your explicit-value constructor is working properly.
Comment these tests as Exercise 9.5. In particular, try a division-by-zero case to see that it prints an error
appropriately. After you have verified that your program has the desired behavior, comment out the test case that
causes the program to exit.

Note that when the programmer creates a fraction object representing one-half, they do not pass
`1/2`

to the explicit-value constructor, as in:

Thenew Fraction(1/2) // Incorrect - This only has one argument (whose value is 0!)

`Fraction`

class knows very well how to represent and work with fractions; it does not need the programmer to tell it what to do
with the numerator and denominator, all it needs is the (separate!) values for the numerator and the denominator, as
in:
new Fraction(1, 2) // Correct - This passes the numerator and denominator for proper handling by the class.

Classes generally declare their instance variables to be private, but it is nevertheless useful at times for
programmers to be able to get those instance values for special uses. This behavior is implemented using *accessors*,
which are simple methods that receive nothing from the calling program and return the value of the instance variable.

Write two accessor methods for your fraction class, one for
`myNumerator`

and the other for
`myDenominator`

. Name these methods following the convention for accessors. When they are implemented, document them and add lines
to your console program to test them.

Some methods make changes to the state of a class object. These methods are called *mutator* methods. For the
fraction class, it will prove convenient to have a mutator method that simplifies the current state of the fraction.
For example, if the fraction is 2/4 (i.e., the numerator is 2 and the denominator is 4), then we’d really like
to be able to simplify the fraction to the value 1/2. You’ll use this method in your explicit-value constructor
to simplify new fraction objects. For example, if a programmer calls
`new Fraction(2, 4)`

, we would like the explicit-value constructor to automatically simplify this to 1/2.

Simplifying fractions requires that you be able to compute the greatest common divisor of the numerator and the denominator. For example, the numerator 2 and denominator 4 share 2 as their greatest common divisor, which means that we can divide both by 2 to simplify 2/4 to (2/2)/(4/2) = 1/2. The greatest common divisor can be computed using the following recursive method.

/** * This method finds the greatest common divisor of two integers, using * Euclid’s algorithm * * @param alpha one integer * @param beta the other integer * @return the greatest common divisor of alpha and beta. */ private static int computeGCD(int alpha, int beta) { alpha = Math.abs(alpha); beta = Math.abs(beta); int remainder = alpha % beta; while (remainder != 0) { alpha = beta; beta = remainder; remainder = alpha % beta; } return beta; }

This method is marked as
`private`

meaning that it will only be used by other methods in the fraction class, in this case only by your new simplify
method. It is also marked as
`static`

, meaning that this single definition can be shared by all fraction objects; this means that the method cannot
directly access instance variables in the class but must, instead, receive its values as parameters.

Implement a fraction simplification method.

Start by copying the computeGCD() definition shown above directly into your fraction class.

Now, implement the following algorithm:

**Assumptions**:`myNumerator`

and`myDenominator`

have been declared as integer instance variables and`computeGCD()`

has been defined as shown above.**Algorithm**(`simplify()`

)**Set**`gcd`

= the greatest common divisor of the numerator and denominator.

Note, you can compute this by calling`computeGCD(myNumerator, myDenominator)`

.**If**`gcd`

is not equal to 0**then**:**Set**`myNumerator`

=`myNumerator/gcd`

.**Set**`myDenominator`

=`myDenominator/gcd`

.

**If**`myDenominator`

is less than 0**then**:**Set**`myNumerator`

=`myNumerator`

* -1.**Set**`myDenominator`

=`myDenominator`

* -1.

Note that this method does not return anything. It is a mutator method that changes the values of
`myNumerator`

and
`myDenominator`

but returns nothing.

When this method is complete, document it properly and add an invocation for it in your explicit-value constructor
just after you’ve assigned the values to
`myNumerator`

and
`myDenominator`

. The invocation should look like this:

myNumerator = numerator; // These first two statements should already be there. myDenominator = denominator;simplify();// This simplifies the values provided by the calling program.

With this all in place, test it out by adding the following code segment to your console application:

Fraction f3 = new Fraction(2, 4); // Construct a fraction object for two fourths. System.out.println(f3); // Print the object.

This new code segment should output
`1/2`

. Plan and implement a few additional test cases to be sure that your explicit-value constructor is working properly
(as usual, comment these tests as Exercise 9.7). In particular, try a case in which both the numerator and
denominator are set to negative numbers; step 3 in the algorithm is a clever bit of coding that should handle
negatives appropriately.

There are times when fractions must interact with other fractions. The methods in this section are not mutator methods. They create a new fraction with the appropriate value.

Multiplying one fraction with another should produce a new fraction with the appropriate numerator and denominator. For example, 1/2 * 2/3 = 2/6 which simplifies to 1/3.

Implementing multiplication requires that we use that odd mechanism common to object-oriented programming languages
without operator overloading in which we must ask one of the fractions to multiply itself by another. The notion for
this is
`fraction1.multiply(fraction2)`

.

Build a fraction multiplication method that implements the following algorithm:

**Assumptions**:`myNumerator`

and`myDenominator`

have been declared as integer instance variables and that the explicit-value constructor simplifies the fractions it constructs.**Algorithm**(`multiply()`

)**Receive**fraction object`otherFraction`

by which to multiply myself.**Return**a new fraction object whose:- numerator is equal to
`myNumerator`

*`otherFraction.getNumerator()`

, and - denominator is equal to
`myDenominator`

*`otherFraction.getDenominator()`

.

- numerator is equal to

As usual, document this new method, and then include a couple test cases for your multiplication method in your console application (document the tests as Exercise 9.8).

Submit all the code and supporting files for the exercises in this lab. We will grade this exercise according to the following criteria:

**Correctness:**- 10% - Attributes - The class should have the appropriate instance variables.
- 10% - Output - The class should have an appropriate
`toString()`

method. - 15% - Constructors - The class should have default and explicit-value constructors.
- 10% - Accessors - The class should have both needed accessors.
- 15% - Mutators - The class should implement
`simplify()`

appropriately. - 10% - Arithmetic Methods - The class should implement
`multiply()`

appropriately. - 15% - Test Cases - The console application should specify sufficient test cases.

**Understandability:**- 5% - Header Documentation - Document the code’s basic purpose, authors and assignment number.
- 5% - Code Documentation - Separate the logical blocks of your program with useful comments and white space.
- 5% - Method Documentation - Each method should have appropriate documentation.