Assignment 10

Task

Revisit A04, only this time make the cone its own class and create instances of it.

Concepts: Constructors, instance variables, encapsulation
Textbook: 4.1 - 4.3, 4.5

Steps

You are going to create a Cone class, test that it is correct, and then use that class to solve a problem involving ice-cream cones. Do this in the following order.

Step 1: Cone.java

Write a public class named Cone. Put this in its own file (for now). The class should have at least two private instance variable for the radius and height. The class should also contain the following public constructor and methods:

Your methods must match these signatures exactly. (You can change the parameter names r and h if you like, but not their order or what they represent.) Note how, unlike in A04, the methods are not static and the get methods no longer take parameters. This is because every cone now already knows its own radius and height, so you don't need to pass this information to the methods anymore.

Your methods should return the correct values. The necessary formulas were given in A02. They should not print anything to the screen or ask for any input from the user.

Error-checking of passed values is optional. For example, you could prevent negative values for the radius by setting it to 0 or taking its absolute value instead; but this is not required--you may allow another programmer to make a cone with a negative radius if they really want to.

Step 2: Testing

The primary reason for writing methods is so that you can easily reuse chunks of code. It reduces errors and makes code easier to maintain if you can avoid duplicating code.

But the separation of code into small, independent modules has additional benefits. It can make the code more readable overall. And you can then unit test each of these small pieces to know they are correct. This helps greatly when debugging, especially for larger programs.

Here is a sample unit test for your cone: ConeTester.java.

Download this file and put it in the same directory as your Cone.java file. You should then be able to compile and run ConeTester to ensure that all your methods have the correct signatures and return the correct values. Take a look at how the ConeTester works because you will be responsible for testing your own classes in future assignments.

Step 3: Main program (UsernameA10.java)

Now that you know your Cone is correct, you could reuse it in a number of different programs. Here is one such program.

An ice cream company sells a pre-packaged ice cream cone similar to a Cornetto or Drumstick: a waffle cone coated with chocolate on the inside and filled with ice cream to form a flat top even with the top of the cone. The company has a long-standing Original size, but they would now like to add a smaller and larger-sized cone to their line:

ConeRadius (cm)Height (cm)
Original312
Small2.510
Large416

However, someone pointed out that this change in cone size might affect the ratio of waffle cone to ice cream, and thus change the flavor of the treat. The company wants to know what the exact numbers are on this. So that's your job.

Specifically, for each of the 3 sizes, print:

Additionally, on the same line as each measure, print what % this is of the Original cone's corresponding measure. Print this % as an int. Round it with Math.rint; don't just cast it. And remember to include the % sign.

You may also print out the radius and height for each cone, if you wish. This is optional, and so you do not need to include the % for these two measures (although you may).

Use your Cone class for all calculations, so that the only literals you will have in main will be the radii given above. There is no user input for this program. Note that you may not need to call every Cone method to solve this particular problem.

Sample Output

Dimensions of 3 ice cream cones (as % of Original): 

ORIGINAL
Ice cream volume: 113.09733552923255 cm^3 (100%)
Waffle cone area: 116.5780650907367 cm^2 (100%)
Ice cream to cone ratio: 0.970142500145332 (100%)

SMALL
Ice cream volume: 65.44984694978736 cm^3 (58%)
Waffle cone area: 80.95698964634494 cm^2 (69%)
Ice cream to cone ratio: 0.8084520834544433 (83%)

LARGE
...

This is only the first half of the output, just to show you a sample format that meets the requirements. You'll need the dimensions of the LARGE cone as well.

What to Submit

You are going to submit only your UsernameA10 and Cone classes.

Normally, each Java class should be in its own file. However, Tamarin only takes a single file per submission. So you are going to move your Cone class into your UsernameA10.java file. Paste the entire Cone class after the last } of the UsernameA10 class. (Please make sure you don't put one class within another!)

Java will allow you to have only 1 public class per file though, so you also need to remove the word public from the Cone class. (Leave the public modifier on all your methods, though.)

So, your UsernameA10.java file should now be structured like this:


public class UsernameA10 {
  //... your main method in here ...
}

class Cone {
  //... your cone methods in here ...
}

You may want to try deleting all your .class files, and then compiling and running your UsernameA10.java one more time before you submit it, just to make everything still works correctly.

Then, upload your complete UsernameA10.java file to Tamarin.

Grading [7 points]

1 - Compiles
Your program compiles successfully (no errors).
0.8 - Cone Constructor
Has a public constructor that takes the radius and height (in that order) as doubles.
1.2 - Cone accessor (get) and mutator (set) methods for radius and height
0.3 each for get and set method, with the method signatures specified above.
1.6 - Cone accessors for slant height, volume, lateral surface area, and total surface area
0.4 each. All methods are instance methods (not static) and exactly match the method signatures given above. Returned values are correct even after changing the dimensions of the cone using the set method.
0.2 - No printing from Cone
Cone methods and constructor should not print to the screen.
0.2 - Encapsulation
All your instance variables are private
2.0 - UsernameA10
Program clearly prints the correct volume, lateral surface area, and ratio of these two for the 3 requested cones (0.9). For each dimension printed, also gives the percentage (as a rounded integer) that this is of the Original cone's corresponding measure (0.45). Creates and uses at least one Cone object to do this (0.35). All calculations are performed by the program, not by you. (0.3).

FAQs

Demo code:
In Sections 001 & 003, we went over a Square example during the second lab this week. However, that is a little too close to a Cone; I don't want you to just copy and paste the code without understanding it.

So here is a different example class--HighScore--from the first lab this week. It has been expanded and commented to show the encapsulation principles at work. Then, to show how such a class might be used, see ArcadeGame.java

For an older and more complicated example, you can also check out PetBlob.java and PetStore.java

What is the purpose of a constructor?
Try reading this.
Advice: Write a method instead of manually duplicating code.
You need to do the exact same sequence of operations to print out the details and %s for each ice cream cone. And you need to repeat this three times with only the specific cone changing each time. This means it would be a good idea to write this code as a static helper method in your UsernameA10 class. Then you only have to write this code once, and can then just call the method three times, passing the method a different cone to work with each time. Since all the code then occurs only once, you only have to write it once. Also, it is much easier to correct and maintain, rather than trying to fix 3 different copy-and-pasted versions.

So, for example, you could write a method like this:

  public static printIceCreamDetails(Cone selected, Cone base)

In this method, print out the lateral surface area, volume, and ratio of the selected cone. Also calculate the % of each in terms of the base cone. Then, in main, after you create the 3 ice cream Cones, you can call this method like this:

  System.out.println("ORIGINAL");
  printIceCreamDetails(original, original);
  System.out.println();
  System.out.println("SMALL");
  printIceCreamDetails(small, original);
  //...

This will save you a fair amount to editing/code tweaking time. Note too how easy it would then be to change your program later to include more cones, different cones, or to determine the %s based on a cone other than the Original. This is the advantage of breaking your code into methods instead of manually duplicating code. (Note how the ConeTester class above did the same sort of thing with the test and equals methods.) This example also demonstrates how you can pass whole objects around to other methods.

So the printIceCreamDetails method described above can print to the screen? Shouldn't methods just return their results?
It depends on the intent of the method. If the class as a whole is meant to model an object or if the method is just doing some computation, it is generally cleaner to have those methods just return their results. That way the class and/or methods can be reused more easily, even in cases where you no longer want the results printed on the screen.

On the other hand, sometimes you just want to move code out of main into a separate method. This serves two purposes. First, it can make main easier to read. Instead of a big block of code, there is just a single method call:

  System.out.println("SMALL");
  printIceCreamDetails(small, original);

This makes is easier to quickly understand what main is doing. Then, to see the particular details of how ice cream cone details are printed, you can go look at that method.

The second purpose of moving code from main into a method is that you can then easily repeat that code by calling the method a number of times. This is certainly true for printIceCreamDetails.

So, since printIceCreamDetails is a helper method to main and its purpose is to help you generate output, it is fine to let it print to the screen for you.

Why do we have to write the getRadius() and setRadius(double r) methods?
It's to practice encapsulation.

You should make your instance variables private. When you do this, you can no longer affect them directly from another class. But it'd still be nice to be able to change the size of a Cone after you construct it, or to ask a Cone object what its radius is. So you write a couple public methods that lets other classes affect the instance variables only through those methods.

This practice of encapsulation is a good long-term programming practice for large projects. Making your instance variables private means you effectively hide them so you can later change how your class works internally without having to also change all the code in every class that uses that changed class (since they use only the methods). An example of this is that your accessor methods for volume and slant height may hide the fact that you don't even have a variable for those dimensions of the Cone; instead, you simply calculate them each time the value is requested through the accessor method.

Also, you can error-check any changes someone is making to a variable in the mutator method. For example, you could enforce in your setRadius method that no one ever sets the radius to a negative value. This is something you could not control if another class had direct access to the variable.

Finally, you could prohibit changes to some variables all together by simply not writing a mutator method for it. For example, no one can directly change the volume of your Cone; instead, they can only change the radius or height.

So, for all these reasons, encapsulation is good practice--particularly for large projects where you have 10s or 100s of classes. So that's why we're having you do it here--to get into the habit.

Why do I need the getTotalSurfaceArea() method or some of these other methods I never called to solve the ice cream problem?
The intent of this assignment is to write a Cone class you could then reuse to a number of different contexts. The ice cream problem is then just to give you some practice using the Cone class in one specific context. So, yes, there are some methods in the Cone class you won't call for A10.

If the ice cream problem were instead the focus of A10, then you'd probably write a IceCreamCone (rather than Cone) class and then you would not need to implement any methods you weren't going to use for that particular assignment. (However, while designing such an IceCreamCone class, you would still want to think about additional ways an IceCreamCone might be used so that your design would allow for easy expansion if it was ever reused elsewhere.)