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

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;

}

}