Assignment 10

Task

Revisit A04, only this time make the sphere 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 Sphere class, test that it is correct, and then use that class to solve a problem involving planets. Do this in the following order.

Step 1: Sphere.java

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

Your methods must much these signatures exactly (though you can change the variable name r, if you like). Note how, unlike in A04, the methods not static and the get methods no longer take parameters. This is because every sphere now already knows its own radius, so you don't need to pass this information to the methods anymore.

Your methods should return the correct values. 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 can 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: SphereTester.java.

Download this file and put it in the same directory as your Sphere.java file. You should then be able to compile and run SphereTester to ensure that all your methods have the correct signatures and return the correct values. Take a look at how the SphereTester 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 Sphere is correct, you could reuse it in a number of different programs. Here is one such program:

The four terrestrial planets in our solar system include:

PlanetMean Radius (km)% of Earth's radius
Mercury244038
Venus605295
Earth6371100
Mars338653

Assuming these planets were perfect spheres, write a program that clearly prints the radius, surface area, and volume for each of the four terrestrial planets. For each measure, also indicate afterwards on the same line what % this is of Earth's measure. Print this % as an int. (Round it with Math.rint; don't just cast it. And remember to include the % sign.) Use your Sphere 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 Sphere method to solve this particular problem.

Sample Output

Dimensions of the 4 terrestrial planets (as % of Earth's):

EARTH
Radius: 6371.0 km (100%)
Surface Area: 5.1006447190978825E8 km^2 (100%)
Volume: 1.0832069168457537E12 km^3 (100%)

MERCURY
Radius: 2440.0 km (38%)
Surface Area: 7.481514408964877E7 km^2 (15%)
Volume: 6.0849650526247665E10 km^3 (6%)

...

This is only the first half of the output, just to show you a sample format that meets the requirements. You'll need VENUS and MARS as well. Planet order is up to you.

If you want to format the long doubles for printing, please still include at least 3 digits 4 digits after the decimal point.

What to Submit

You are going to submit only your UsernameA10 and Sphere 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 Sphere class into your UsernameA10.java file. Paste the entire Sphere 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 Sphere 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 Sphere {
  //... your sphere 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 [6 points]

1 - Compiles
Your program compiles successfully (no errors).
0.5 - Sphere Constructor
Has a public constructor that takes the radius as a double.
0.8 - Sphere accessor (get) and mutator (set) methods for radius
0.4 each for get and set method.
1.6 - Sphere accessors for diameter, circumference, volume, and 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 sphere using the set method.
0.3 - No printing from Sphere
Sphere methods and constructor should not print to the screen.
1.8 - UsernameA10
Program clearly prints the correct radius, surface area, and volume of the four terrestrial planets (0.6). For each dimension printed, also gives the percentage (as a rounded integer) that this is of Earth's corresponding dimension (0.6). Creates and uses at least one Sphere object to do this (0.4). All calculations are performed by the program, not you. (0.2).

FAQs

Demo code:
In Sections 001 & 004, we went over a Square example during the second lab this week. However, that is a little too close to a Sphere; 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

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 planet. And you need to repeat this four times with only the specific planet 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 four times, passing the method a different planet/sphere 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 4 different copy-and-pasted versions.

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

  public static printPlanetDetails(Sphere planet, Sphere basePlanet)

In this method, print out the radius, surface area, and volume of planet. Also calculate the % of each in terms of basePlanet. Then, in main, after you create the 4 planet Spheres, you can call this method like this:

  System.out.println("EARTH");
  printPlanetDetails(earth, earth);
  System.out.println();
  System.out.println("MERCURY");
  printPlanetDetails(mercury, earth);
  //...

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 planets, other planets, or determine the %s based on a planet other than Earth. This is the advantage of breaking your code into methods instead of manually duplicating code. (Note how the SphereTester class above did the same sort of thing.) This example also demonstrates how you can pass whole objects around to other methods.

So the printPlanetDetails method descibed 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("EARTH");
  printPlanetDetails(earth, earth);

This makes is easier to quickly understand what main is doing. Then, to see the particular details of how planet 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 printPlanetDetails.

So, since printPlanetDetails is 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 Sphere after you construct it, or to ask a Sphere 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 surface area may hide the fact that you don't even have a variable for those dimensions of the Sphere; 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 Sphere; instead, they can only change the radius.

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 we need the getCircumference() and getDiameter() methods? They aren't necessary for the planets problem.
The intent of this assignment is to write a Sphere class you could then reuse to a number of different contexts. The planets problem is then just to give you some practice using the Sphere class in a specific context. So, yes, there are some methods in the Sphere class you won't call for A10.

If the planet's problem were instead the focus of A10, then you'd probably write a Planet (rather than Sphere) 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 a Planet class, you would still want to think about additional ways a Planet might be used so that your design would allow for easy expansion if it ever was reused elsewhere.)

I formatted my doubles and now Tamarin is giving me trouble.
Yeah, I should have been more explicit about the requirements on this. As it is, don't worry about it: as long as you got the right results, I'll correct Tamarin regardless of how the decimal is formatted.

If you're using a DecimalFormat object to format your doubles, the format string you select will produce very different output. Here are some examples when formatting the double values 1.0832069168457537E12 and 5.1006447190978825E8 (Earth's volume and surface area).

FormatSample Output
#1083206916846510064472
#.###1083206916845.754510064471.91
0.0001083206916845.754510064471.910
0.###E01.083E125.101E8
0.0000E01.08321E125.1006E8

The # means a number, which can be dropped/not displayed if it is 0. Using 0 instead forces that digit/number to be displayed, even if it is 0. What Tamarin is searching for is the 0.000 prefix, assuming the numbers are displayed in the E format. Since DecimalFormat rounds for you, you should actually use "0.0000E0" as your formatting string to avoid any problems.

But, like I said, as long as your results are correct, I'll correct Tamarin if it's just failing due to format.