By completing this exercise you will understand better these subjects:

·         How to use single dimensional arrays of primitive types.

·         How to use single dimensional arrays of object references.

·         Using Java’s enumeration type Enum.

·         Writing your own classes.

·         Writing utility classes and static methods (and using them).

·         Understanding the difference between interface (API) and implementation, and the beauty of encapsulation.

·         Testing your code.

·         Using a non-generic collection (ArrayList) and casting between an Object and your own class.

·         First implementation of the overridden method toString.

·         Dividing the code to packages and accessing it across packages.

The exercise consists of a weight rack, which is like a shelf that holds weights in the gym (see image above). There are many kinds of weights, but all have a label on them with how much they weigh. However, some of them provide the weight in kilograms (“kg”), some in pound (“lb”), and others. For example, a weight rack can hold 4 weights of 10kg and 2 weights of 15lbs. You will have to implement such a weight rack that can hold all kinds of weights, and provide the gym with basic services, such as adding and removing weights to the rack or getting some statistical information about the weights on the rack.

 

We first lay the infrastructure for our project with a utility class, which will reside in a special utility package. This class will provide us methods of unit conversions.

Write a package called weight_utils. You do that simply by creating a subfolder with the same name in your exercise folder. Write a utility class called WeightUtilities in that package. Since we defined this class as a utility class, we will only implement static methods in it. Implement the following API:

·         public enum WeightType {KILOGRAM, POUND}

o   Enumeration of all the possible weight types.

o   Remember that enumerations are always open for adding more values.

·         public static float kilosToPounds(float kilos)

o   Converts the given weight from kilograms to pounds.

·         public static float poundsToKilos(float pounds)

o   Converts the given weight from pounds to kilograms.

·         There are 2.2 pounds in one kilogram.

·         Remember to use final constants.

·         Adding a class to a package is done by placing the file in the package subfolder, and declaring at the top of the file that it’s part of that package.

·         Since we’re dealing with small numbers, we choose float as our data type, because it’s more precise on smaller numbers than double.

You can use the following main method to test your code. Take it as an example for the future, where you will be asked to test you own code. Plant the code in any class you want:

      public static void main(String[] args) {

            // convert kilos to pounds

            System.out.print("10 kilos (should be 22 pounds): ");

            System.out.println(WeightUtilities.kilosToPounds(10) + " lbs.");

            System.out.print("11 kilos (should be 24.2 pounds): ");

            System.out.println(WeightUtilities.kilosToPounds(11) + " lbs.");

           

            // convert pounds to kilos

            System.out.print("22 pounds (should be 10 kilos): ");

            System.out.println(WeightUtilities.poundsToKilos(22) + " kgs.");

            System.out.print("24 pounds (should be 10.909091 kilos): ");

            System.out.println(WeightUtilities.poundsToKilos(24) + " kgs.");

           

            // testing special cases

            System.out.println("Testing conversion of 0 weight: ");

            System.out.println(WeightUtilities.kilosToPounds(0) + " lbs.");

            System.out.println(WeightUtilities.poundsToKilos(0) + " kg.");

      }

Your output should look like this:

10 kilos (should be 22 pounds): 22.0 lbs.

11 kilos (should be 24.2 pounds): 24.2 lbs.

22 pounds (should be 10 kilos): 10.0 kgs.

24 pounds (should be 10.909091 kilos): 10.909091 kgs.

Testing conversion of 0 weight:

0.0 lbs.

0.0 kg.

 

This implementation of our weight rack is using an array of primitive types. The rack can hold many weights of different kinds, but up to a maximum capacity (these things are heavy!).

Create a new package called gym. Write a class called PrimitiveWeightRack in that package.

Represent the internal data structures by using these instance variables (you have to initialize them, either in the declaration statement or in the constructor):

private static final int MAXIMUM_WEIGHTS = 20;

private float[] weights;

private WeightType[] weightTypes;

private int numberOfWeights;

Since we don’t know in advance how many weights there are going to be on our rack, we will have to handle it appropriately. We do that by following 3 rules:

1.       We assume a maximum number of weights (the MAXIMUM_WEIGHTS constant), and initialize our array with that size.

2.       We track the current number of weights with a variable (numberOfWeights). This will also be the index of the next available cell in our array.

3.       To avoid “holes” in our array, we always make sure, in our algorithms, that when we remove an element, we shift all the elements “to the left” so that the array is kept without holes.

We use two arrays, one array (weights) holds the weights themselves, and the other one (weightTypes) holds the different weight types. Each element in one array is respective to the same element in the other array (so that the type of weights[5] is kept in weightTypes[5]. We have to make sure that rule is kept within our code.

Implement the following API:

·         public PrimitiveWeightRack(float capacityInKilos)

o   This is the only constructor of this class.

o   You have to validate the parameter, that is, make sure it’s valid.

o   A valid capacity has to be positive, but not larger than a reasonable number, say, a ton.

o   If the parameter is invalid, print an appropriate message and exit the program by writing the statement System.exit(0);

·         public boolean addWeight(float weight, WeightType weightType)

o   Adds a single weight to the weight rack, with the given weight of the given type.

o   If the weight was successfully added to the rack, we return true. Otherwise, the method returns false.

o   You have to make sure that the given weight is positive, and that by adding that weight to the rack we don’t exceed the rack capacity. In case of an error, print the error message to the console and return false.

·         public boolean removeWeight(float weightInKilos)

o   Removes a given weight from the rack.

o   Find a weight with the same weight as the parameter, whether in kilograms or an equivalent in other types. For example, if someone asks to get the 10kg weight, and we found a 22lbs weight, we remove that one instead. The gymnast doesn’t care what’s written on the weight as long as they get to practice with the appropriate weight.

o   The method returns true if the required weight was found. If the given parameter is invalid or the weight was not found, print an appropriate message and return false.

·         public String toString()

o   This method builds a String of the following format:

§  One line per weight.

§  Each line holds the number, then a blank space, then either “kg” or “lb”, according to that specific weight type.

§  At the end: “Total weights: “, and the total number of weights on the rack. Then a dot.

o   Sample output:

4.0 lb

5.0 kg

50.0 kg

Total weights: 3.

·         public float totalWeight(WeightType type)

o   Calculates and returns the total weight of all the weights currently on the rack.

o   The result should be calculated according to the given type.

·         Although our Enum currently holds only 2 values, remember that we usually use enumeration when we plan to add more values. Therefore, whenever we inquire about a specific enumerated value, we are encouraged to use switch..case rather than if..else.

·         To use the class and enum types from the utility package we wrote in the previous section, we have to import them.

·         To remove a weight, you can use, for example, the given method System.arraycopy. Don’t forget to leave no holes in the array!

The gym is where many people pay a lot of money, but only 10% of them actually claim the service (it’s like insurance).

Our gym holds weight racks, and can be used to test our racks and give you the romantic feeling of encapsulation.

Write a class called Gym in the gym package.

The Gym only holds one main, and even that is provided:

public static void main(String[] args) {

      PrimitiveWeightRack rack = new PrimitiveWeightRack(1000);

      rack.addWeight(2, WeightType.KILOGRAM);

      rack.addWeight(4, WeightType.POUND);

      rack.addWeight(5, WeightType.KILOGRAM);

      rack.removeWeight(2);

      rack.removeWeight(10);

      rack.addWeight(10, WeightType.KILOGRAM);

      rack.addWeight(22, WeightType.POUND);

      rack.removeWeight(10);

      rack.removeWeight(10);

      rack.addWeight(50, WeightType.KILOGRAM);

      System.out.println(rack);

      System.out.println("Total weight in kilos: "

                  + rack.totalWeight(WeightType.KILOGRAM));

      System.out.println("Total weight in libros: "

                  + rack.totalWeight(WeightType.POUND));

}

 

Test it to see you get satisfying results.

Note this statement:

System.out.println(rack);

We can always provide our entire rack object when a String is needed, and Java will know to call the toString method, if we implemented it. Neat, huh?

Now it’s time to advance in the level of elegance. Since ‘a weight’ is a real life entity, the best design would be writing a class dedicated to representing a weight. We can then use it in a more advanced (and elegant) rack.

Write a class called Weight in the package gym. The class holds only a weight value (float) and its type, and provides some service. Inspect the services in the following API and implement them:

·         public Weight(float weight, WeightType type)

o   This constructor builds a new weight with the given weight and type.

o   Naturally, if the weight is not positive, we have to print an appropriate message and exit the program (it’s best to take the same message we used in the previous section for the same case).

·         public String toString()

o   Returns a String representation of this weight.

o   Examples:

§  5.0 kg

§  32.444 lb

o   There should not be a newline character (“\n”) at the end of the String.

·         public WeightType getType()

o   Returns the type of this weight.

·         public float getWeightInKilos()

o   Returns the weight in kilograms (convert if needed).

·         public float getWeightInPounds()

o   Returns the weight in pounds (convert if needed).

·         Nothing to guide. It’s the simplest class it can be.

·         Use the static methods of the utility class you’ve already written (don’t forget to import).

You can use the main method below in your Weight class to test your code. Uncomment the first lines, and see what happens. Use a calculator to make sure the results are OK.

      public static void main(String[] args) {

            // Weight w1 = new Weight(-1, WeightType.KILOGRAM);

            // Weight w2 = new Weight(0, WeightType.KILOGRAM);

           

            /** Testing a non-integer kilo */

            Weight w3 = new Weight(100.5f, WeightType.KILOGRAM);

            System.out.println("w3: " + w3);

            System.out.println("Type: " + w3.getType());

            System.out.println("Weight in kilos: " + w3.getWeightInKilos());

            System.out.println("Weight in pound: " + w3.getWeightInPounds());

           

            /** Testing an integer kilo */

            Weight w4 = new Weight(20, WeightType.KILOGRAM);

            System.out.println("w4: " + w4);

            System.out.println("Type: " + w4.getType());

            System.out.println("Weight in kilos: " + w4.getWeightInKilos());

            System.out.println("Weight in pound: " + w4.getWeightInPounds());

           

            /** Testing an integer pound */

            Weight w5 = new Weight(20, WeightType.POUND);

            System.out.println("w5: " + w5);

            System.out.println("Type: " + w5.getType());

            System.out.println("Weight in kilos: " + w5.getWeightInKilos());

            System.out.println("Weight in pound: " + w5.getWeightInPounds());

      }

This is a more elegant (and ‘classy’) version of the primitive weight rack. This implementation will use collections of class Weight, rather than primitive collections.

In the gym package, write a class called ClassyWeightRack. Use this instance variable to hold the weights:

      private ArrayList weights;

You may need some more instance variables, namely the current and maximum capacity. You will also need to initialize them, either on the declaration statement or in the constructor.

Implement the following API:

·         public ClassyWeightRack(float capacityInKilos)

o   Constructs a new rack with the given capacity.

o   Validate the capacity and behave exactly the same as with the primitive rack.

·         Implement the entire API (except the constructor) of class PrimitiveWeightRack. The method signatures and descriptions are similar, but the implementation may differ.

The ArrayList collection holds elements of type Object. However, we force the array to hold elements of type Weight. When we iterate the ArrayList, our current element will be of type Object, and we will have to cast it into our own class. For example:

            ArrayList turtles = new ArrayList();

            for (Object object : turtles) {

                  Turtle t = (Turtle)object;

                  if (t.isTailDown())

                        t.tailUp();

            }

We should also consider a possibility that the cast failed, and instead of receiving a reference to Turtle, we get null.

Now here comes the fun part:

In your Gym class, replace this line:

PrimitiveWeightRack rack = new PrimitiveWeightRack(1000);

With this:

ClassyWeightRack rack = new ClassyWeightRack(1000);

Compile and run. If it doesn’t compile, go back and correct class names, method names or parameters. If it compiles, it should give you the exact same result as it was before the modification.

This is the beauty of encapsulation.

Implement a method which calculates the average weight of all weights of a given type on a rack. For example, if a rack has 3 weights, 1 of which is 10 pounds and the other two are 2 and 4 kilos, then the method returns ‘10’ if the parameter is pounds, and ‘3’ if the parameter is kilos.

In order to get bonus points, you have to implement this method in both PrimitiveWeightRack and in ClassyWeightRack, and both should work correctly with no bugs. If even one of them has a bug, you will get no bonus points at all. The method signature:

·         public float getWeightAverage(WeightType type)

If the rack has no weights on it, print an appropriate message and return 0.

As always, read the submission guidelines and styling conventions in our website.

Since we have two packages here, your softcopy should include two folders with the following structure:

·         Ex04.zip (the file you are sending):

o   weight_utils (folder)

§  WeightUtilities.java

o   gym (folder)

§  PrimitiveWeightRack.java

§  Weight.java

§  ClassyWeightRack.java

You can create such a zip file by selecting both subfolders, right click them and choose to Zip both in to an archive named Ex04.zip.

Remember, your hard copy must be identical to your code!

Good luck!! Description: http://t1.gstatic.com/images?q=tbn:ANd9GcQvajbbOuzQYJXGRcLfFrwqHRDQOuCNLakVGw1r8GvqSRel2h0&t=1&usg=__XvRvMFbTDjckaE6XnzNkTD2y-i0=

Boaz.

 

 

© 2010 Boaz Kantor, IDC