Skip to content

4.3 Tutorial 5

practice_guide_5.pdf

Task 1: Consider project better-ticket-machine from chapter 2 of the book Objects First with Java: A Practical Introduction using BlueJ (Barnes & Kölling). Modify the class TicketMachine​ to include pre- and post-conditions for all methods. Add those specifications as part of the documentation and using exceptions instead of assertions. Propose a class invariant, add it as part of the documentation and implement its validation as a repOk​ method.

/**
 * TicketMachine models a ticket machine that issues
 * flat-fare tickets.
 * The price of a ticket is specified via the constructor.
 * Instances will check to ensure that a user only enters
 * sensible amounts of money, and will only print a ticket
 * if enough money has been input.
 * 
 * @author David J. Barnes and Michael Kölling
 * @version 2016.02.29
 */
public class TicketMachine
{
    // The price of a ticket from this machine.
    private int price;
    // The amount of money entered by a customer so far.
    private int balance;
    // The total amount of money collected by this machine.
    private int total;

    /**
     * Create a machine that issues tickets of the given price.
     * <li> cost must greater than 0 <li/>
     */
    public TicketMachine(int cost)
    {
        if(cost <= 0)
        {
            throw new IllegalArgumentException("The cost: " + cost + "is not greater than 0");
        }
        price = cost;
        balance = 0;
        total = 0;
        if(!repOk())
        {
            throw new IllegalStateException("The value of price or balance or total is invalid");
        }
    }

    /**
     * @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 from a customer.
     * Check that the amount is sensible.
     * <li> Amount should be a positive number <li/>
     */
    public void insertMoney(int amount)
    {
        if(amount <= 0)
        {
            throw new IllegalArgumentException("Amount: " + amount + "is not a positive number");
        }
        else
        {
            balance = balance + amount;
        }
        if(!repOk())
        {
            throw new IllegalStateException("Error, the balance becomes negative");
        }
    }

    /**
     * Print a ticket if enough money has been inserted, and
     * reduce the current balance by the ticket price. Print
     * an error message if more money is required.
     */
    public void printTicket()
    {
        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 collected with the price.
            total = total + price;
            // Reduce the balance by the price.
            balance = balance - price;
        }
        else {
            System.out.println("You must insert at least: " +
                               (price - balance) + " more cents.");

        }
        if(!repOk())
        {
            throw new IllegalStateException("The total or balance becomes invalid");
        }
    }

    /**
     * @return the money in the balance.
     * The balance is cleared.
     */
    public int refundBalance()
    {
        int amountToRefund;
        amountToRefund = balance;
        balance = 0;
        return amountToRefund;
    }
    /**
     * @return the money in the Total.
     * The total is cleared.
     */
    public int emptyMachine()
    {
        int originTotal = total;
        total = 0;
        return originTotal;
    }
    /**
     * Check whether the representation is valid.
     * @return (price && balance && total is non-negative)
     */
    public boolean repOk()
    {
        if(price <= 0 || balance < 0 || total < 0)
        {
            return false;
        }
        return true;
    }
}

Task 2: Consider project clock-display from chapter 3 of the book Objects First with Java: A Practical Introduction using BlueJ (Barnes & Kölling). Modify classes ClockDisplay​ and NumberDisplay​ with pre- and post-conditions for all methods. Add these specifications as part of the documentation, using exceptions instead of assertions to check them. Propose a class invariant for both classes, add them as part of the documentation and implement its validation as a repOk​ method.

/**
 * The NumberDisplay class represents a digital number display that can hold
 * values from zero to a given limit. The limit can be specified when creating
 * the display. The values range from zero (inclusive) to limit-1. If used,
 * for example, for the seconds on a digital clock, the limit would be 60, 
 * resulting in display values from 0 to 59. When incremented, the display 
 * automatically rolls over to zero when reaching the limit.
 * 
 * @author Michael Kölling and David J. Barnes
 * @version 2016.02.29
 */
public class NumberDisplay
{
    private int limit;
    private int value;

    /**
     * Constructor for objects of class NumberDisplay.
     * Set the limit at which the display rolls over.
     * <li> rollOverLimit should be greater than 0 <li/>
     */
    public NumberDisplay(int rollOverLimit)
    {
        if(rollOverLimit <= 0)
        {
            throw new IllegalArgumentException("rollOverLimit: " + rollOverLimit 
            + "should be greater than 0");
        }
        limit = rollOverLimit;
        value = 0;
        if(!repOk())
        {
            throw new IllegalStateException("Error, the limit has been changed to non-positive number");
        }
    }

    /**
     * @return the current value.
     */
    public int getValue()
    {
        return value;
    }

    /**
     * @return the display value (that is, the current value as a two-digit
     * String. If the value is less than ten, it will be padded with a leading
     * zero).
     * <li> value should in [0, limit) </li>
     */
    public String getDisplayValue()
    {
        if(value < 0 || value >= limit)
        {
            throw new IllegalArgumentException("The value: " + value + "should" + 
            "less than limit and greater or equal than zero");
        }
        if(value < 10) {
            return "0" + value;
        }
        else {
            return "" + value;
        }
    }

    /**
     * Set the value of the display to the new specified value. If the new
     * value is less than zero or over the limit, do nothing.
     * <li> replacementValue should in [0, limit) <li/>
     */
    public void setValue(int replacementValue)
    {
        if((replacementValue < 0) || (replacementValue >= limit)) {
            throw new IllegalArgumentException("ReplacementValue should in [0, limit)");
        }
        else
        {
            value = replacementValue;
        }
        if(!repOk())
        {
            throw new IllegalStateException("The value has been non-positive, error");
        }
    }

    /**
     * Increment the display value by one, rolling over to zero if the
     * limit is reached.
     */
    public void increment()
    {
        value = (value + 1) % limit;
        if(!repOk())
        {
            throw new IllegalStateException("The value has been non-positive, error");
        }
    }
    /**
     * Check whether the representation is valid.
     * @return (limit is non-positive && value greater or equal to limit && value is negative)
     */   
    public boolean repOk()
    {
        if(limit <= 0)
        {
            return false;
        }
        else if(value >= limit || value < 0)
        {
            return false;
        }
        return true;
    }
}

Task 3: Consider project music-organizer-v2 from chapter 4 of the book Objects First with Java: A Practical Introduction using BlueJ (Barnes & Kölling). Modify classes MusicOrganizer​ and Track​ with pre- and post-conditions for all methods. Add these specifications as part of the documentation, using exceptions instead of assertions. Propose a class invariant for both classes, add them as part of the documentation and implement its validation as a repOk​ method.

/**
 * Store the details of a music track,
 * such as the artist, title, and file name.
 * 
 * @author David J. Barnes and Michael Kölling
 * @version 2016.02.29
 */
public class Track
{
    // The artist.
    private String artist;
    // The track's title.
    private String title;
    // Where the track is stored.
    private String filename;

    /**
     * Constructor for objects of class Track.
     * @param artist The track's artist.
     * @param title The track's title.
     * @param filename The track file. 
     * <li>
     * artist, title, filename cannot be null
     * artist, title, filename cannot be empty
     * </li>
     */
    public Track(String artist, String title, String filename)
    {
        if(artist.trim().isEmpty() || title.trim().isEmpty() || filename.trim().isEmpty())
        {
            throw new IllegalArgumentException("Artist, title, filename cannot be empty!");
        }
        else if(artist == null || title == null || filename == null) 
        {
            throw new IllegalArgumentException("Artist, title, filename cannot be null!");
        }
        setDetails(artist, title, filename);
        if(!repOk())
        {
            throw new IllegalStateException("Artisit or title or filename becomes invalid");
        }
    }

    /**
     * Constructor for objects of class Track.
     * It is assumed that the file name cannot be
     * decoded to extract artist and title details.
     * @param filename The track file. 
     * <li>
     * filename cannot be null, filename cannot be empty
     * </li>
     */
    public Track(String filename)
    {
        if(filename.trim().isEmpty())
        {
            throw new IllegalArgumentException("filename cannot be empty!");
        }
        else if(filename == null) 
        {
            throw new IllegalArgumentException("filename cannot be null!");
        }
        setDetails("unknown", "unknown", filename);
        if(!repOk())
        {
            throw new IllegalStateException("filename becomes invalid");
        }
    }

    /**
     * Return the artist.
     * @return The artist.
     */
    public String getArtist()
    {
        return artist;
    }

    /**
     * Return the title.
     * @return The title.
     */
    public String getTitle()
    {
        return title;
    }

    /**
     * Return the file name.
     * @return The file name.
     */
    public String getFilename()
    {
        return filename;
    }

    /**
     * Return details of the track: artist, title and file name.
     * @return The track's details.
     */
    public String getDetails()
    {
        return artist + ": " + title + "  (file: " + filename + ")";
    }

    /**
     * Set details of the track.
     * @param artist The track's artist.
     * @param title The track's title.
     * @param filename The track file. 
     * <li>
     * artist, title, filename cannot be null
     * artist, title, filename cannot be empty
     * </li>
     */
    public void setDetails(String artist, String title, String filename)
    {
        if(artist.trim().isEmpty() || title.trim().isEmpty() || filename.trim().isEmpty())
        {
            throw new IllegalArgumentException("Artist, title, filename cannot be empty!");
        }
        else if(artist == null || title == null || filename == null) 
        {
            throw new IllegalArgumentException("Artist, title, filename cannot be null!");
        }
        this.artist = artist;
        this.title = title;
        this.filename = filename;
        if(!repOk())
        {
            throw new IllegalStateException("Artist, title or filename become invalid");
        }
    }
    /**
     * Check whether the representation is valid.
     * @return (whether artist && title && filename arenot null && artist && title && filename arenot empty
     */  
    public boolean repOk()
    {
        if(artist.trim().isEmpty() || title.trim().isEmpty() || filename.trim().isEmpty())
        {
            return false;
        }
        if(artist == null || title == null || filename == null) 
        {
            return false;
        }
        return true;
    }

}

Arrays and loops

Task 4: Consider the accompanying code utils. Complete the implementations for all methods.

Task 5: Consider if the class should be divided to increase cohesion, do it if you consider it necessary.

Task 6: Should methods require the creation of an object? Justify your answer.

Task 7: If the answer for the previous question was negative, made any necessary modifications.

Task 8: Make a new class to test all methods, try difference scenarios for each.

NumberUtils.java

import java.util.ArrayList;
import java.util.Arrays;
public class NumberUtils
{

    public boolean isPrime(int n) {
        if(n <= 0) {
            throw new IllegalArgumentException("The number must be positive");
        }
        boolean isPrime = true;
        if(n == 1) {
            return false;
        }
        else {
            for(int divisor = 2; divisor < n; divisor++) {
                if(n % divisor == 0) {
                    isPrime = false;
                }
            }
        }
        return isPrime;
    }

    public int nthFib(int n) {
        if(n <= 0) {
            throw new IllegalArgumentException("The number must be positive");
        }
        if(n == 1) {
            return 0;
        }
        if(n == 2) {
            return 1;
        }
        else {
            return nthFib(n - 1) + nthFib(n - 2);
        }
    }

    public ArrayList<Integer> digits(int n) {
        if(n < 0) {
            throw new IllegalArgumentException("The number must be greater or equal than zero");
        }
        ArrayList<Integer> digits = new ArrayList<>();
        if(n == 0) {
            digits.add(0);
        }
        else {
            int n2 = n;
            int digit;
            while(n2 != 0) {
                digit = n2 % 10;
                digits.add(digit);
                n2 /= 10;
            }
        }
        return digits;
    }

ArrayUtils.java

import java.util.ArrayList;

/**
 * This class offers some basic array routines.
 * 
 * @author N. Aguirre
 * @version 0.2
 */
public class ArrayUtils {

    /**
     * Returns the minimum value in a non-empty array of floats.
     * preconditions:
     * <ul>
     * <li>The array cannot be {@code null}, nor empty.</li>
     * </ul>
     * @param values the array of floats.
     * @return {@code m} such that {@code m in values} and there is no {@code n in values}
     *         such that {@code n < m}.
     */
    public static float minimum(float[] values) {
        if(values == null || values.length == 0){
           throw new IllegalArgumentException("The array cannot be null, nor empty"); 
        }
        float min = values[0];
        for(int i = 1; i < values.length; i++){
            if(values[i] < min){
                min = values[i];
            }
        }
        return min;
    }

    /**
     * Returns the maximum value in a non-empty array of floats.
     * preconditions:
     * <ul>
     * <li>The array cannot be {@code null}, nor empty.</li>
     * </ul>
     * @param values the array of floats.
     * @return {@code m} such that {@code m in values} and there is no {@code n in values}
     *         such that {@code n > m}.
     */
    public static float maximum(float[] arreglo) {
        if(arreglo == null || arreglo.length == 0){
           throw new IllegalArgumentException("The array cannot be null, nor empty"); 
        }
        float max = arreglo[0];
        for(int i = 1; i < arreglo.length; i++){
            if(arreglo[i] > max){
                max = arreglo[i];
            }
        }
        return max;
    }

    /**
     * Returns the first n values of the Fibonacci sequence.
     * preconditions:
     * <ul>
     * <li>n must be greater or equal than zero (0).</li>
     * </ul>
     * @param n a value that determines the first n values of the Fibonacci sequence to return.
     * @return the first {@code n} elements of the Fibonacci sequence.
     */
    public static int[] fibSequence(int n) {
        if(n < 0){
           throw new IllegalArgumentException("n must be greater or equal than zero"); 
        }
        //!!!Note the base case
        if (n == 0) {
            return new int[0];
        }
        if (n == 1) {
            return new int[]{0};
        }
        int[] fibSeq = new int[n];
        fibSeq[0] = 0;
        fibSeq[1] = 1;
        for(int i = 2; i < n; i++){
            fibSeq[i] = fibSeq[i - 1] + fibSeq[i - 2];
        }
        return fibSeq;
    }

    /**
     * Returns the average value of a non-empty array of floats.
     * preconditions:
     * <ul>
     * <li>The array cannot be {@code null}, nor empty.</li>
     * </ul>
     * @param values the array of floats.
     * @return the average value for {@code values}.
     */
    public static float average(float[] values) {
        if(values == null || values.length == 0){
           throw new IllegalArgumentException("The array cannot be null, nor empty"); 
        }
        float avg = 0;
        for(int i = 0; i < values.length; i++){
            avg += values[i];
        }
        avg /= values.length;
        return avg;
    }

    /**
     * Returns the median value of a non-empty array of floats.
     * preconditions:
     * <ul>
     * <li>The array cannot be {@code null}, nor empty.</li>
     * </ul>
     * @param values the array of floats.
     * @return the median value for {@code values}.
     */
    public static float median(float[] values) {
        if(values == null || values.length == 0){
           throw new IllegalArgumentException("The array cannot be null, nor empty"); 
        }
        //Bubble Sort
        for(int i = 0; i < values.length; i++){
            for(int j = 0; j < values.length - 1; j++){
                if(values[j] > values[j + 1]){
                    float temp = values[j];
                    values[j] = values[j + 1];
                    values[j + 1] = temp;
                }
            }
        }

        if(values.length % 2 == 0){
            return (values[values.length / 2] + values[values.length / 2 - 1]) / 2;
        }
        else {
            return values[(values.length - 1) / 2];
        }
    }

    /**
     * Checks if two integer arrays are equal.
     * preconditions:
     * <ul>
     * <li>The arrays cannot be {@code null}</li>
     * </ul>
     * @param arr1 the first array of integers.
     * @param arr2 the second array of integers.
     * @return {@code true} if both {@code arr1} and {@code arr2} have the same values in the same order.
     */
    public static boolean equals(int[] arr1, int[] arr2) {
        if(arr1 == null || arr2 == null){
           throw new IllegalArgumentException("The array cannot be null"); 
        }
        if(arr1.length != arr2.length){
            return false;
        }
        boolean isEqual = true;
        for(int i = 0; i < arr1.length; i++){
            if(arr1[i] != arr2[i]){
                isEqual = false;
            }
        }
        return isEqual;
    }

    /**
     * Checks if an array has repeated values.
     * preconditions:
     * <ul>
     * <li>The array cannot be {@code null}</li>
     * </ul>
     * @param values the array to check.
     * @return {@code true} if {@code values} contain repeated values.
     */
    public static boolean hasRepeatedValues(int[] values) {
        if(values == null){
           throw new IllegalArgumentException("The array cannot be null"); 
        }
        boolean isRepeated = false;
        for(int i = 0; i < values.length; i++){
            for(int j = i + 1; j < values.length; j++){
                if(values[i] == values[j]){
                    isRepeated = true;
                }
            }
        }
        return isRepeated;
    }

    /**
     * Checks if an array has two different values that sum zero (0).
     * preconditions:
     * <ul>
     * <li>The array cannot be {@code null}</li>
     * </ul>
     * @param values the array to check.
     * @return {@code true} if exists {@code n in values} and {@code m in vales}
     *          such that {@code n != m} and {@code n + m == 0}.
     */
    public static boolean hasOpposites(float[] values) {
        if(values == null){
           throw new IllegalArgumentException("The array cannot be null"); 
        }
        boolean isOpposite = false;
        for(int i = 0; i < values.length; i++){
            for(int j = i + 1; j < values.length; j++){
                if(values[i] == -values[j]){
                    isOpposite = true;
                }
            }
        }
        return isOpposite;
    }

}