M/F 2:30-3:20   
in Philips 101

CS 1130: Transition to OO Programming

Spring 2015

Monitoring Rhinos

Due to CMS by Friday, February 6th.

Rhino Rhino Family The rhinoceros population is endangered, having declined 90 percent since 1970, with only five species remaining the world today. The white and black rhinos are the only species left in Africa. Much of the threat to the rhinoceros comes from poachers, who kill them for their horns. Recently, Kenya (with less than 500 rhinos left!) relocated 33 rhinos to Meru National Park, where they hope to protect them from poachers.

When an animal population is small or endangerd, the animals are typically monitored in order to track the population. Sometimes these animals are captured and tagged. Some tags emit a signal, so that one can track the animal. Even in populated places, animals are tagged. Her in Ithaca, there are deer with tags on their ears wandering in the fields. If you keep your eyes peeled, it will not take long for you to see one.

Your task in this assignment is to develop a Java class Rhino, which will maintain information about a rhino for population mointoring. In addition, you will make a JUnit class RhinoTester to maintain a suite of testcases for Rhino. This assignment will help illustrate how Java's classes and objects can be used to maintain data about a collection of things.


Table of Contents


Before You Get Started

Read Carefully

In order to give you as much guidance as possible, these instructions are fairly long. Your chances of completing the assignment quickly be increased by reading carefully and following all instructions. Many requests for resubmission are caused not by issues with programming but simply by not following instructions.

We have tried to lay out the instructions in sections to make it clear as possible. You should pay particular attention to the section on Iterative Development, as it contains important instructions for the remaining sections, and we will not repeat these instructions for each section.

Grading Policy (Revise-and-Resubmit Cycle)

To ensure that everyone masters this assignment, we will use an iterative feedback process. If one of the objectives below is not properly met, we will give you feedback and expect you to revise and resubmit. This process will continue until you are done. This process should be finished by 18th of February; once you finish you will receive a perfect score of 10. In our experience, almost everyone is able to achieve a perfect score within two submissions.

In grading your code, we will focus on the following issues:

  • If the class invariants (e.g. field specifications), method specifications, or formatting are not appropriate, we will ask that you fix them.
  • Once the specifications and formatting are okay, we will look at your test cases. If they are inadequate, we will ask you to fix them, test your program again yourself with the new cases, and resubmit.
  • If the test cases are adequate, we will test your program with our own testing program. If there are errors, we will ask you to correct them and resubmit.

Formatting is graded according to the course style guidelines, available on the course web page.

Until we have decided that you have mastered (e.g. 100/100) the assignment, your "grade" on CMS will be the number of revisions so far. This allows us to we can keep track of your progress. Do not be alarmed if you see a "1" for the assignment at first! The assignment will be considered completed when it passes all three steps outlined above.

Collaboration Policy

You may do this assignment with one other person. If you are going to work together, then form your group on CMS as soon as possible. This must be completed before you submit the assignment. Both people must do something to form the group. The first person proposes, and then the other accepts.

If you do this assignment with another person, you must work together. It is against the rules for one person to do some programming on this assignment without the other person sitting nearby and helping. Take turns "driving"; alternate using the keyboard and mouse.

With the exception of your CMS-registered partner, you may not look at anyone else's code or show your code to anyone else, in any form what-so-ever.

Assignment Scope

Everything that you need to complete this assignment should have been covered by Lecture 4 in class, or the first module in the web lectures. While you may have been exposed to them before you may not use if-statements anywhere in this assignment, as they are not necessary. Submissions containing if-statements will be returned for you to revise.

Getting Help

If you do not know where to start, if you do not understand testing, or if you are completely lost, please see someone immediately. This can be the course instructor, a TA, or a consultant. Do not wait until the last minute. A little in-person help can do wonders. See the staff page for more information.


Working with DrJava

To do this assignment, DrJava must be set up properly. If you have not already done this, follow the DrJava instructions to set it up on your computer.

Next, you should adjust your DrJava preferences to make programming a littler easier. Open up the Preferences window, found in the Edit Menu. First, change the indent level; choose "Miscellaneous" in the Preferences window and change the entry "Indent Level" to 4. In addition, you should choose "Display Options" and check the box "Show All Line Numbers".

Once you do that, you will find that you can properly format the code in your entire file simply by hitting the tab key once; DrJava will format everything automatically. Given the size of these tabs, however, you may find yourself with long lines that require you to scroll to the right to read them. We do not want to have to scroll to the right, so you should break up long lines (including both code and comments) into two or more smaller lines.

Finally, you will want to create a folder on your hard drive that is dedicated to this assignment and this assignment only. Every time that you work on a new assignment, we want you to make a new folder, as otherwise it is easy to mix up your assignments.


Initial Draft of Rhino

The first thing to get start is to create a skeleton class definition for class Rhino. This is a class declaration with nothing -- no fields, constructors, or methods -- in between the two outer braces. In addition, you should add some import comments to this file.

First, you should add a file comment, which includes your name and the date on which you completed the assignment. This should be a single-line comment (e.g. using the // prefix) as the first first line of the file. Below that line you have a second single-line comment that attests whether or not JavaDoc functions correctly.

Next you should add a JavaDoc class comment. A JavaDoc comment is a multi-line comment in-between /** and */. This comment should go on the line before the public class... declaration. This comment should contain the following text verbatim:

Instances of this class are monitoring data for a single rhino.

Save this file into the new directory and compile it. You will want to compile often as you proceed. In general it is a good idea to compile every time you complete an objective.

Class Fields

The next thing to do when making a class is to create the fields. All of these fields should be private and properly commented. We have more instructions on commenting below. The fields for this class are as follows:

name (a String)
This is the name (first, last, and perhaps middle) for this profile. This should not be the empty string. It is possible for different profiles to have the same name.
year of birth (an int)
This a four-digit number, like 1857 or 2005. Do not worry about invalid dates. You should not write code that checks whether dates are valid; assume they are valid.
month of birth (an int)
This is month in which the person in this profile was born. It should be a number between 1..12, with 1 meaning the month of January.
tag (an int)
This is the number on the rhino's tag. This is an integer ≥ 0. If the rhino is not tagged yet, this field contains -1.
gender (a char)
This must either be 'M' for male or 'F' for female.
mother (a Rhino)
This is a name of an instance of class Rhino that is the mother of this Rhino object. It is null if this mother is unknown.
father (a Rhino)
This is a name of an instance of class Rhino that is the father of this Rhino object. It is null if this father is unknown.
number of children (an int)
This is a number that depends on the mother and father fields of other Rhino objects.

Class Invariant

Every field declaration should be accompanied by a comment. This comment should describe what the field means, the constraints on the field, and the legal values for the field. This type of comment is called an \emph{invariant}. The collection of invariants for all fields is called the class invariant

For example, for the month-of-birth field, state in a comment that the field contains the month of birth and that it must be in the range 1..12. Here is an example of a declaration with a suitable comment:

private char gender; // The gender of this rhino;
                     // 'M' for male and 'F' for female
Note the we do not JavaDoc style comments for fields in this class.

Do not include things like "(an int)" in a comment for a field. We only included that above so that you know what type the field should be. It is not needed in the comment because the type is obvious from the declaration.


Iterative Development

You are not quite done yet with Rhino, but no one ever writes a complete class without testing parts of it first. For the rest of the assignment, you will go through an iterative development cycle. This means that you will write a little bit of the class, and then fully test it before you write any more. This process makes it easier to find bugs; you know that any bugs must have been part of the work you did since the last test. This section is an overview of what you need to do for the rest of this assignment.

Skeleton of RhinoTester

Iterative development hinges on proper JUnit testing. Create a new JUnit class in DrJava and call this class RhinoTester. DrJava might provide you with a skeleton of a test procedure (e.g. a procedure with nothing inside the inner braces) called testX(). You are to remove this test procedure, and replace it with five test methods: testPartA, testPartB, testPartC, testPartD, and testPartE. Each of these corresponds to a part of the assignment described below. Each test procedure should be empty for now; you will fill in the tests later.

Instructions for the Remainder of the Assignment

The rest of the assignment is broken into five parts (listed Part A, B, C, D, and E). In each part, you will work on a group of methods in Rhino. In each part, you will do the following:

  1. Write the Javadoc specifications for each method given. Make sure they are complete and correct; look at the specifications we give you. You can even copy and paste.
  2. Write the methods themselves.
  3. Use one of the test procedures in RhinoTester for all of the methods in this part. Add test cases to this procedure for all of the methods given in this part
  4. Run the JUnit test and make sure everything is correct.

Remember: To run a JUnit test, have the class you are testing -- Rhino --- selected in DrJava and hit the "Test" button. DrJava will ask you for the class to test with and you will select RhinoTester.

Writing Method Specifications

The descriptions that we provide in each part below represent the level of completeness and precision we are looking for in your Javadoc comments. In fact, it is best to copy and paste these descriptions to create the first draft of your Javadoc comments. If you do not cut and paste, please adhere to the conventions we use, such as using the prefixes "Constructor: ...", for constructors and "Yields: ..." for functions. You should also use double-quotes to enclose an English boolean assertion. Using a consistent set of good conventions in this class will help us all.

In our specifications, there are no references to specific field names, since the user may not know what the fields are, or even if there are fields. The fields are private. Remember the class JFrame: you know what methods it has, but not what fields, and the method specifications do not mention fields. In the same way, a user of your class Rhino will know about the methods but not the fields.


Part A: Constructor and Getters

The names of your methods must match those listed below exactly, including capitalization. The number of parameters and their order must also match: any mismatch will cause our testing programs to fail, meaning that you will have to resubmit. Parameter names will not be tested; you can change the parameter names if you want.

Constructor

For this part, you will write a single constructor. It will have the following form:

Rhino(String n, int y, int m, char g)
The specification for this method is as follows:
Constructor: a new rhino with name n, birth year y, birth month m, and gender g. Its parents are unknown, and it has no children.
Precondition: n's length is > 0. m is in the range 1..12. g is either `M' (for male) or `F' (for female)

Your constructor should initialize all of the fields in the profile object to satisfy the specification above. You should \textbf{not write code that checks whether preconditions hold}. It is the responsibility of the caller to ensure that the precondition is met. This means, for example, that you should not worry about (or write code that checks for) a name that is null.

Getters

In addition to the constructor, you should write several getter methods. The complete list of methods to write for this part is as follows:

Getter Method Description (JavaDoc Specification) Return Type
getName() Yields: the name for this rhino String
getYear() Yields: the year the rhino was born int
getMonth() Yields: the month the rhino was born. 1 means Jan, 2 means Feb, etc. int
isMale() Yields: "this rhino is a male" boolean
getTag() Yields: this rhino's tag (>= 0) if defined; -1 if it is untagged int
getMother() Yields: the mother of this rhino (null if unknown) Rhino (not String!)
getFather() Yields: the father of this rhino (null if unknown) Rhino (not String!)
getNumChildren() Yields: the number of children for this profile int

Testing

Once you have created the constructor and getters, it is time to create your JUnit test. Your first test will be in the procedure testPartA in the RhinoTester. This procedure should first create a Rhino object (with the constructor). It should then use the getter methods to test that the fields all have the correct values. A by-product of writing the test this way is that you will test both the constructor and the getters at the same time.

There are eight fields, so you should be able to do this with eight assertEquals statements, which we showed in the lectures. Note from the constructor specification that the eight fields should all have an exact value after the constructor; nothing should be unknown. Hence you do not need to use assertTrue.


Part B: Setters

Three of the fields in Rhino were missing from the constructor. That means we need setters for these three fields so that they can be assigned later. The setters that you need to write are as follows:

Setter Method Description (JavaDoc Specification)
setTag(int i) Set the tag for this rhino to i. Precondition: i >= 0
addMother(Rhino p) Add p as the mother of this rhino. Precondition: p is not null, p is a female, and this rhino's mother is currently null.
addFather(Rhino p) Add p as the father of this rhino. Precondition: p is not null, p is a male, and this rhino's father is currently null.

Again, you should not write code to check whether the preconditions holds. For example, addMother should not check that p is female. In addition, we are not asking you to write methods that change an existing mother or father to a different Rhino. Both of these require if-statements and if-statements are not allowed in this assignment.

Hint: The methods addMother and addFather are actually a bit tricky. Not only do you need to change the fields of this profile, but you also need to change a field of the parent profile p. Look at the fields again and see why this is the case.

Testing

You will test these setter methods in the test procedure testPartB. When testing the setter methods, you will have to create one or more Rhino objects, call the setter methods, and then use the getter methods to test whether the setter methods set the fields correctly. Hence, it is a good thing you already tested the getters!

As we said, the methods addMother and addFather change more than one field. Your testing procedure should check that all fields that may be changed are changed correctly.


Part C: Another Constructor

Your next goal in this assignment to write a second constructor for the class. This constructor is a thorough constructor that initializes all of the fields, and not just a select few. It will have the following form:

Rhino(String n, int y, int m, char g, int tag,
      Rhino ma, Rhino pa)
The specification for this constructor is as follows:
Constructor: a new rhino with name n, birth year y, birth month m, gender g, identifier id, mother ma, and father pa.
Precondition: n's length is > 0. m is in the range 1 .. 12. g is either `M' (for male) or `F' (for female). tag >= 0 (or -1 if untagged). ma and pa may not be null.

Testing

The next round of testing should occur in the test procedure testPartC. As part of this test, you will need to create an Rhino using the second constructor. Note that this requires that you first create two Rhino objects using the first constructor, to serve as the mother and father. Once you have done this, you should again use the getters to test that all of the fields have been set properly.

As before, you do not need to check that the preconditions are satisfied. That is the responsibility of the person using this class, not the person writing it.


Part D: toString

By default, when you look at a Rhino object in the interactions pane, you can see the "folder name" of the object. We want to change that, and have a more descriptive representation of Rhino instances. To do this, we write a function toString() with no parameters that produces a String representation of the Rhino object.

We are not going to tell you exactly how you should design the toString() method. However, the examples below give you some idea of what we are looking for in this assignment:

  • "Male rhino Fatso. Born 6/2005. Has 1 child."
  • "Female rhino Daisy. Born 4/2005. Has 1 child."
  • "Female rhino Annie. Tag 34. Born 8/2006. Mother Ellen. Father Doug. Has 0 children."
There are several rules that you need to follow when making toString():
  • You may not use an if-statement. However, you will use conditional expressions, and we give you one of them below.
  • This function is best written with a single return statement that consists of the catenation of several parts. For readability, you should each part on a separate line. In developing the return statement, it is best to work with one part at a time, making sure it is right before proceeding to the next part.
  • Exactly one blank appears between words (with the exception of the profile name, which can have any spacing within it, depending on what is in the field). Periods '.' are as indicated in the examples.
  • You can get the gender with a blank after it using this conditional expression:
    (isMale() ? "Male " : "Female ")
  • The word "rhino" is followed by a blank, the name in the profile, a period `.', and a blank.
  • The tag is given in the form "Tag <integer>. " It appears only if the rhino has a tag (e.g. is not -1).
  • The birth month and year always appear, as in the examples.
  • Suppose the mother of a profile is known and has the name "xxx ". Then "Mother xxx. " should appear in the child profile, as in the examples. Similarly for the father. If the mother (father) is not known, nothing should be said about it. The mother appears first.
  • The number of children appears as shown in the examples, with a period after it. When it is 1 child, use "child"; otherwise, use "children".

Testing

The next round of testing should occur in the test procedure testPartD. In testing toString(), you need enough test cases to ensure that each different way of evaluating a conditional expression is tested. For example, to test whether the gender appears correctly, you need at least two test cases, for a female Rhino and for a male Rhino. You should do similar things for the number of children, and the parentage of the Rhino.

When making up a test case, construct it by hand, based on what you read above. Then, write an assertEquals statement that compares this expected value to the computed one. This is what we will do in our test program.


Comparison Functions

The last part of this assignment is to write the two functions in the table below. Each produces a boolean value and compares one Rhino to another.

The comparison functions obey the following rules:

  • The names of your methods much match those listed above exactly, including capitalization. The number of parameters and their order and types must also match. The best way to ensure this is to copy and paste. Our testing will expect those method names and parameters, so any mismatch will cause our testing program to fail. Parameter names are never tested, so you can change the parameter names if you want.
  • Each method must be preceded by an appropriate specification, as a Javadoc comment. The best way to ensure this is to copy and paste from this handout. After you have pasted, be sure to do any necessary editing.
  • You may not use if-statements, conditional expressions, or arithmetic operations (e.g. addition, multiplication, division). The purpose is to practice using boolean expressions, with operators && (AND), || (OR), and ! (NOT).

The methods for this part of the assignment are as follows:

Method Description (JavaDoc Specification)
isOlder(Rhino p) Yields: "p is not null and this Rhino is older than p".
isOlder(Rhino p, Rhino q) Yields: "p and q are not null and p is older than q".
Make this function static and write it using the previous isOlder(Rhino) as a helper method.

Testing

When you are done with the last part of this assigment, you should test it in the test procedure partE. When you test the comparison methods, you should test them on a diverse and representative set of inputs. In other words, think of all the different ways that the comparison can be false, and all of the different ways in which it can be true; you should have at least one test for each case.


Finishing the Assignment

Once you have everything working you should go back and make sure that your program is properly formatted so that we can easily read it. In particular, you should check that the following are all true:

  1. All lines short enough that horizontal scrolling is not necessary (about 80 chars is long enough).
  2. There is a blank line before the specification of each method and no blank line after the specification.
  3. The class invariant is appropriate (e.g. you have listed constraints for all the fields).
  4. The specifications for all of the methods are complete.
  5. You have created a JavaDoc version and put a comment at the top of the class.
It is also a good idea for you to remind yourself of the course style guide.

The last step requires some additional explanation.

JavaDoc Creation

Recall that JavaDoc is how we create the web pages that you see in the Java API. In this class, we use it mainly to check that you have filled in all of your specifications correctly. Click the Javadoc button in DrJava and examine the output. You should see your method and class specifications.

Read through them from the perspective of someone who has not read your code, and fix them if necessary so that they are appropriate to that perspective. You must be able to understand everything there is to know about writing a call on each method from the specification that you see (e.g. without knowing anything about the private fields). Thus, the fields should not be mentioned.

Once you have done this, add a comment at the very top of your code saying that you checked the Javadoc output and it was OK. We will hold you to this, and if the JavaDoc is not okay, the assignment will be sent back for revision.

Turning it In

Upload files Rhino.java and RhinoTester.java on the CMS by the due date: Friday, February 6th at 11:59PM. Do not submit any files with the extension/suffix .java~ (with the tilde) or .class. It will help to set the preferences in your operating system so that extensions always appear.

Check the CMS daily until you get feedback from a grader. Make sure your CMS notifications for CS 1130 are set so that you are sent an email when one of your grades is changed.

Within 24 hours, you should do the four Rs: Read the feedback, Revise your program accordingly, Resubmit, and Request a Regrade using the CMS. If you do not request a regrade, we have no simple way of knowing that you have resubmitted. The whole process should be finished within one week.