Solution #4

Gym

4.1      WeightUtilities.java

 

package weight_utils;

 

/**

 * This utility class provides weight related services, such as weight unit

 * conversions.

 *

 * @author Boaz Kantor

 */

public class WeightUtilities {

      private static final float POUNDS_IN_KILOS = 2.2f;

 

      /**

       * Represents all the different supported weight units.

       */

      public enum WeightType {

            KILOGRAM,

            POUND

      }

     

 

      /**

       * Converts kilograms to pounds.

       * @param kilos the weight in kg

       * @return the weight in lbs

       */

      public static float kilosToPounds(float kilos) {

            return kilos * POUNDS_IN_KILOS;

      }

 

      /**

       * Converts pounds to kilograms.

       * @param pounds the weight in lbs

       * @return the weight in kg

       */

      public static float poundsToKilos(float pounds) {

            return pounds / POUNDS_IN_KILOS;

      }

}


 

4.2      PrimitiveWeightRack.java

 

package gym;

 

import weight_utils.WeightUtilities;

import weight_utils.WeightUtilities.WeightType;

 

/**

 * Represents a rack of weights.

 * A weight rack can hold multiple weights of heterogeneous types (units).

 * It also has a maximum weight (capacity) and a maximum number of weights it

 * can hold.

 *

 * @author Boaz Kantor

 */

public class PrimitiveWeightRack {

 

      /** Denotes the maximum capacity the rack can hold */

      private static final float MAXIMUM_CAPACITY = 1000;

      /** Denotes the maximum number of weights the rack can hold */

      private static final int MAXIMUM_WEIGHTS = 20;

 

      // instance variables

      private float capacityInKilos = 0;

      private float[] weights = new float[MAXIMUM_WEIGHTS];

      private WeightType[] weightTypes = new WeightType[weights.length];

      private int numberOfWeights = 0;

 

      /**

       * Constructs a new weight rack object with the given weight capacity.

       * @param capacityInKilos the maximum weight the rack can hold

       */

      public PrimitiveWeightRack(float capacityInKilos) {

           

            // validate parameters

            if (capacityInKilos <= 0 || capacityInKilos > MAXIMUM_CAPACITY) {

                  System.out.println("Invalid capacity " + capacityInKilos + " kilos.");

                  System.exit(0);

            }

           

            this.capacityInKilos = capacityInKilos;

      }

 

      /**

       * Adds a new weight to the rack.

       * A weight can be added if, by adding it, the rack does not exceed its

       * capacity or the maximum number of weights it can hold.

       * @param weight the weight value

       * @param weightType the weight type (unit)

       * @return true if the weight was added successfully, otherwise false

       */

      public boolean addWeight(float weight, WeightType weightType) {

           

            // validate parameters

            if (weight < 0) {

                  System.out.println("A weight must weigh something!");

                  return false;

            }

           

            float weightInKilos = 0;

           

            // although you can use a simple 'if' here, when checking a value

            // of an enum, it's always good to use a switch..case statement, which

            // lays the foundation of additional enum values.

            switch (weightType) {

                  case KILOGRAM:

                        weightInKilos = weight;

                        break;

                  case POUND:

                        weightInKilos = WeightUtilities.poundsToKilos(weight);

                        break;

                  default:

                        weightInKilos = weight;

                        break;

            }

           

            // check if we're not over capacity

            if (totalWeight(WeightType.KILOGRAM) + weightInKilos > capacityInKilos) {

                 

                  // we're over capacity

                  System.out.println("Weight rack is over capacity.");

                  return false;

            }

           

            // add the weight to the rack

            weights[numberOfWeights] = weight;

            weightTypes[numberOfWeights++] = weightType;

           

            return true; // the weight was successfully added

      }

     

      /**

       * Removes a weight from the rack with the given weight. Although the weight

       * is denoted in kilograms, any weight of equivalent value is suitable for

       * removal. The first suitable weight found is removed.

       * @param weightInKilos the weight, in kilograms, of the weight to remove

       * @return true if a weight with equivalent value was removed, otherwise

       * false

       */

      public boolean removeWeight(float weightInKilos) {

           

            // validate parameters

            if (weightInKilos <= 0) {

                  System.out.println("A weight must weigh something!");

                  return false;

            }

           

            int indexToRemove = 0;

            boolean found = false;

           

            // iterate through the entire collection of weights

            for (int currentWeight = 0;

                        currentWeight < numberOfWeights && !found;

                        ++currentWeight) {

                 

                  // compare the current weight to the given weight according to its

                  // type (unit).

                  switch (weightTypes[currentWeight]) {

                        case KILOGRAM:

                             

                              // see if the current weight is the same as the required

                              if (weightInKilos == weights[currentWeight]) {

                                   

                                    // it is. remember which weight we have to remove.

                                    indexToRemove = currentWeight;

                                    found = true;

                              }

                              break;

                        case POUND:

                             

                              // see if the current weight is equivalent to the required

                              if (WeightUtilities.kilosToPounds(weightInKilos) ==

                                          weights[currentWeight]) {

                                   

                                    // it is. remember which weight we have to remove.

                                    indexToRemove = currentWeight;

                                    found = true;

                              }

                              break;

                  }

            }

           

            // check if we found a suitable weight to remove

            if (!found) {

                 

                  // no such weight found

                  System.out.println("Could not find a weight of " + weightInKilos

                              + " kilos.");

                  return false;

            }

           

            // remove weight and reorder rack so there are no "holes" in the array.

            // Note: another good implementation is moving the last weight to fill

            // the hole made by the removed weight.

            System.arraycopy(weights, indexToRemove + 1,          // source

                        weights, indexToRemove,                   // destination

                        numberOfWeights - indexToRemove - 1);     // how many

            System.arraycopy(weightTypes, indexToRemove + 1,      // source

                        weightTypes, indexToRemove,               // destination

                        numberOfWeights - indexToRemove - 1);     // how many

           

            // never forget to update your instance variables!

            numberOfWeights--;

           

            return true;

      }

 

      /**

       * Returns a String representation of the weight rack.

       */

      public String toString() {

            String result = "";

 

            // add each weight to the String separately

            for (int currWeight = 0; currWeight < numberOfWeights; ++currWeight) {

                  result += weights[currWeight] + " ";

                  switch (weightTypes[currWeight]) {

                        case KILOGRAM:

                              result += "kg"; // better as a final

                              break;

                        case POUND:

                              result += "lb";

                              break;

                        default:

                              result += "unknown";

                              break;

                  }

                  result += "\n"; //newline

            }

           

            // add the summarizing line

            result += "Total weights: " + numberOfWeights + ".";

           

            return result;

      }

     

      /**

       * Returns the total weight on the rack, calculated according to the given

       * type (unit).

       * @param type the weight unit to calculate the total weight according to

       * @return the total weight on the rack

       */

      public float totalWeight(WeightType type) {

           

            // summarize in kilograms, then convert to the required unit

            float sumInKilo = 0;

           

            // add each weight on the rack

            for (int currWeight = 0; currWeight < numberOfWeights; ++currWeight) {

                  switch(weightTypes[currWeight]) {

                        case KILOGRAM:

                              sumInKilo += weights[currWeight];

                              break;

                        case POUND:

                              sumInKilo += WeightUtilities.poundsToKilos(

                                          weights[currWeight]);

                              break;

                  }

            }

           

            // convert to the required unit (no need for 'break' after 'return')

            switch (type) {

                  case KILOGRAM:

                        return sumInKilo; // no need to convert

                  case POUND:

                        return WeightUtilities.kilosToPounds(sumInKilo);

                  default:

                        return sumInKilo; // my own decision. you can decide otherwise.

            }

      }

 

      /**

       * BONUS: Returns the average weight of all weights of the given unit.

       * @param type the weights of that type we calculate the average of

       * @return the average weight of all the given weights of the required unit

       */

      public float getWeightAverage(WeightType type) {

            float sum = 0;

            int count = 0;

 

            // iterate through the entire rack

            for (int currWeight = 0; currWeight < numberOfWeights; ++currWeight) {

                 

                  // see if the current weight is of the required type

                  if (weightTypes[currWeight] == type) {

                       

                        // it is. include it in the calculations.

                        sum += weights[currWeight];

                        count++;

                  }

            }

           

            // see if there are relevant weights at all (and avoid division by zero)

            if (count == 0) {

                  System.out.println("No weights on the rack.");

                  return 0;

            }

 

            // return the average

            return sum / count;

      }

}

 

 

 


 

4.4      Weight.java                    

 

package gym;

 

import weight_utils.WeightUtilities;

import weight_utils.WeightUtilities.WeightType;

 

/**

 * Represents a single weight.

 * A weight object has its own weight value and type, where the type can be

 * any of the supported units.

 *

 * @author Boaz Kantor

 */

public class Weight {

     

      // instance variables

      private WeightType type = WeightType.KILOGRAM;

      private float weight = 0;

     

      /**

       * Constructs a new weight object with the given value and unit.

       * @param weight the weight itself (value)

       * @param type the weight unit

       */

      public Weight(float weight, WeightType type) {

           

            // validate parameters

            if (weight <= 0) {

                  System.out.println("A weight must weigh something!");

                  System.exit(0);

            }

           

            this.type = type;

            this.weight = weight;

      }

     

      /**

       * Returns the weight type (unit) of this weight object.

       * @return the weight type (unit) of this weight object

       */

      public WeightType getType() {

            return this.type; // 'this' is redundant here

      }

 

      /**

       * Returns the weight (value) of this weight object in kilograms.

       * @return the weight (value) of this weight object in kilograms

       */

      public float getWeightInKilos() {

            switch(getType()) { // always prefer calling your own methods

                              // over accessing your instance variables directly.

                  case KILOGRAM:

                        return weight; // no need for a 'break' after 'return'

                  case POUND:

                        return WeightUtilities.poundsToKilos(weight);

                  default:

                        return 0; // my own decision

            }

      }

     

      /**

       * Returns the weight (value) of this weight object in pounds.

       * @return the weight (value) of this weight object in pounds

       */

      public float getWeightInPounds() {

            switch(getType()) {

                  case KILOGRAM:

                        return WeightUtilities.kilosToPounds(weight);

                  case POUND:

                        return weight;

                  default:

                        return 0;

            }

      }

     

      /**

       * Returns a String representation of this Weight object

       */

      public String toString() {

            String result = weight + " ";

            switch (type) {

                  case KILOGRAM:

                        result += "kg"; // better have this as a final

                        break;

                  case POUND:

                        result += "lb";

                        break;

                  default:

                        result += "unknown"; // you can decide of something else

                        break;

            }

            return result;

      }

}


 

4.5      ClassyWeightRack.java

 

package gym;

 

import java.util.ArrayList;

 

import weight_utils.WeightUtilities;

import weight_utils.WeightUtilities.WeightType;

 

/**

 * Represents a rack of weights.

 * A weight rack can hold multiple weights of heterogeneous types (units).

 * It also has a maximum weight (capacity) and a maximum number of weights it

 * can hold.

 *

 * @author Boaz Kantor

 */

public class ClassyWeightRack {

 

      /** Denotes the maximum capacity the rack can hold */

      private static final float MAXIMUM_CAPACITY = 1000;

      /** Denotes the maximum number of weights the rack can hold */

      private static final int MAXIMUM_WEIGHTS = 20;

 

      // instance variables

      private ArrayList weights = new ArrayList();

      private float capacityInKilos = MAXIMUM_CAPACITY;

     

      /**

       * Constructs a new weight rack object with the given weight capacity.

       * @param capacityInKilos the maximum weight the rack can hold

       */

      public ClassyWeightRack(float capacityInKilos) {

           

            // validate parameters

            if (capacityInKilos <= 0 || capacityInKilos > MAXIMUM_CAPACITY) {

                  System.out.println("Invalid capacity " + capacityInKilos

                              + " kilos.");

                  System.exit(0);

            }

           

            this.capacityInKilos = capacityInKilos;

      }

     

      /**

       * Adds a new weight to the rack.

       * A weight can be added if, by adding it, the rack does not exceed its

       * capacity or the maximum number of weights it can hold.

       * @param weight the weight value

       * @param weightType the weight type (unit)

       * @return true if the weight was added successfully, otherwise false

       */

      public boolean addWeight(float weight, WeightType weightType) {

            float weightInKilos = 0;

           

            // although you can use a simple 'if' here, when checking a value

            // of an enum, it's always good to use a switch..case statement, which

            // lays the foundation of additional enum values.

            switch (weightType) {

                  case KILOGRAM:

                        weightInKilos = weight;

                        break;

                  case POUND:

                        weightInKilos = WeightUtilities.poundsToKilos(weight);

                        break;

                  default:

                        weightInKilos = weight;

                        break;

            }

           

            // verify we're not over capacity

            if (totalWeight(WeightType.KILOGRAM) + weightInKilos

                        > capacityInKilos) {

                 

                  // we're over capacity

                  System.out.println("Weight rack is over capacity.");

                  return false;

            }

           

            // add the weight to the rack

            weights.add(new Weight(weight, weightType));

           

            return true;

      }

 

      /**

       * Removes a weight from the rack with the given weight. Although the weight

       * is denoted in kilograms, any weight of equivalent value is suitable for

       * removal. The first suitable weight found is removed.

       * @param weightInKilos the weight, in kilograms, of the weight to remove

       * @return true if a weight with equivalent value was removed, otherwise

       * false

       */

      public boolean removeWeight(float weightInKilos) {

           

            // iterate through the entire collection

            for (Object object : weights) {

                 

                  // work with a reference to Weight, not to Object

                  Weight weight = (Weight)object;

                 

                  // check if it's the required weight

                  if (weight.getWeightInKilos() == weightInKilos) {

                       

                        // it is. remove it (no need to fill holes in ArrayList)

                        weights.remove(object);

                        return true;

                  }

            }

 

            // if we reached here, no suitable weight was found

            System.out.println("Could not find a weight of " + weightInKilos

                                + " kilos.");

            return false;

      }

 

      /**

       * Returns a String representation of the weight rack.

       */

      public String toString() {

            String result = "";

            for (Object object : weights) {

                  Weight weight = (Weight)object;

                  result += weight + "\n";

            }

            result += "Total weights: " + weights.size() + ".";

            return result;

      }

     

     

      /**

       * Returns the total weight on the rack, calculated according to the given

       * type (unit).

       * @param type the weight unit to calculate the total weight according to

       * @return the total weight on the rack

       */

      public float totalWeight(WeightType type) {

            float sumInKilo = 0;

           

            // iterate through the entire rack

            for (Object object : weights) {

                  sumInKilo += ((Weight)object).getWeightInKilos();

            }

           

            // return the value according to the required unit

            switch (type) {

                  case KILOGRAM:

                        return sumInKilo;

                  case POUND:

                        return WeightUtilities.kilosToPounds(sumInKilo);

                  default:

                        return 0;

            }

      }

     

      /**

       * BONUS: Returns the average weight of all weights of the given unit.

       * @param type the weights of that type we calculate the average of

       * @return the average weight of all the given weights of the required unit

       */

      public float getWeightAverage(WeightType type) {

            float sum = 0;

            int count = 0;

           

            // sum all the relevant weights

            for (Object object : weights) {

                  Weight weight = (Weight)object;

                  if (weight.getType() == type) {

                       

                        // the required unit, so include in calculations

                        sum += weight.getWeightInKilos();

                        count++;

                  }

            }

           

            // see if there are relevant weights at all (and avoid division by zero)

            if (count == 0) {

                  System.out.println("No weights on the rack.");

                  return 0;

            }

     

            // I decided to use 'if' here, nicer than 'switch' in this case, but

            // not crucial. both are OK.

            if (type == WeightType.POUND) {

                  sum = WeightUtilities.kilosToPounds(sum);

            }

           

            // return the calculated average

            return sum / count;

      }

}