Chapter:Wrapping it up

From Juneday education
Jump to: navigation, search

This page is obsoleted (not maintained) and replaced by:

Contents

Assignments

Here the assignments will be published.

Meta information about this chapter

Expand using link to the right to see the full content.

Please see the individual assignments for details, comments and instructions for the teachers and staff.

Assignment 1 - Guessing game

Notes to teachers, supervisors and tutors

Expand using link to the right to see the full content.

Goals with the assignment

The first assignment is aimed at giving the students a task which is simple enough for everyone to successfully finish, while still constituting a piece of software which actually does something and contains realistic parts which structurally resembles an object oriented design of a simple game.

The game is a simple guessing game where the player thinks of a number in some interval. The computer makes educated guesses by asking if its guess is less than or equal to the number the player is thinking of. The player answers (truthfully) T (for true) if the question is correct, and, F (for false) otherwise. Based on this input, the computer uses binary search to zoom in on the number the player is thinking of.

The assignment is constructed so that the student gets an almost complete program but is tasked to complete the missing parts. The instructions are meant to guide the student through this process.

The idea behind making the first assignment easy enough for every student to successfully complete, is to strengthen the student’s self-confidence while still conveying useful insights and techniques for programming in Java. The decision for choosing an interactive game as the program for the assignment, is based on our belief that this gives some meaning to the assignment since the student upon completion can run the software and see it work using different numbers for the computer to “guess”. We think this creates an interest for what else a computer program can do. We have noticed that students find batch-like programs to be strange. A typical batch-like program creates a few objects of some class, typically supplying different state to the objects via the constructor. Then the program asks the objects about their state, and print the state to the screen:

Person p1 = new Person("James");
Person p2 = new Person("Lisa");
System.out.println("p1 is called " + p1.name());
System.out.println("p2 is called " + p2.name());

A typical comments from students regarding the above code is “Yes, but I knew what the names of p1 and p2 were, I created the objects myself on the lines immediately before printing this!”

The only realistic scenario for batch programs as the above is when you write test code (which is part of this lab as a suggestion in one of the hints).

We have made an effort for making also the code and design of the program interesting for the students who want to learn from reading code and trying to understand the whole program.

The decision to make the topic about a game

We have consciously avoided more technically or mathematically focused topics (like prime number generators, simple compression or encryption algorithms, sorting algorithms etc) since it is our firm belief that such topics both could be mathematically and/or technically challenging while at the same time giving an unjustified impression of programming as always being a theoretical and technical activity. This could turn some students off. Note that the binary search used for the guessing part (which is already implemented for the students) contains some technical and mathematical aspects which might be inspiring for those students who would not be discouraged by a more technical assignment.

We have also avoided (what we have found to be) classical assignments of reading data from a file, manipulating the data and then saving it back to a file. Such file-based assignments tend, in our opinion, to be challenging to students for the parts doing file i/o and exception handling compared to the rest of the program. Also, such assignments, we find, tend to be quite mechanical and not the type of programs that the student actually would find interesting to run. Some examples of such assignments include reading a stock inventory file, generate some reports and save them on disk, reading a data file and printing out statistical findings about the data, etc. While such programs have some real life applicability, they are not much fun to run over and over.

For students who find the assignment too easy or not challenging enough, we have a number of non-mandatory optional tasks at the end.

Things we predict will be hard for the students

A classic problem when faced with the first assignment is that it is a lot of text to read and understand. The first question usually is “What should we do?” or “What is the task?”. So be prepared that at least some students will have a hard time getting started because they don’t understand what the assignment is about. The solution to this problem usually is to find a way to encourage the students to read the assignment once again. And then, convince them to start with the first task and work in small increments.

A thing about the game logic which may be hard for some students to understand is the concept of “less than or equal to”. If the player is thinking of the number N and the game asks “Is your number less than or equal to N?” it is possible that some students will miss the “or equal to” part and answer “F” (erroneously, since N is <= N, indeed). This is important that all students understand however, since comparisons usually are central to e.g. the for-statement.

The very first task is to create a constructor from scratch, and the next few tasks are about completing incomplete methods. A problem for the students here, will be to know if they are done with a sub task. For instance, how do they know that they have completed the first task and written a working constructor? In the hints section for the first task, we encourage the students to write a toString() method for Guesser which returns information about the two instance variables low and high (which should be given values via the constructor parameters). Then they can use the toString() to verify that the constructor really saves the parameters correctly. But before they even venture into writing tests, they should be encouraged to make sure that the code they write actually compiles. This, our experience tells us, cannot be stressed enough. Encourage the students to save the source code and compile, and do so often! If they change something, they should make sure that the program compiles, so that they don’t test an old compiled version. The good thing about compiling often, is that small mistakes quickly are caught by the compiler, and knowing how to read compilation errors is a very useful skill.

Students who have great difficulty to write a simple constructor as the one in task 1, should be encouraged to review the chapters “Objects in Java” and in particular “Classes”. If watching the video lectures and doing the exercises again doesn’t help, they need some face time with a tutor. Make sure to take notes about all the difficulties the students have during the assignments, so that you are prepared to customize any repetition sessions you may plan to give before the written exam. Supervision (live or via email) is the best source for tuning extra lectures and repetition sessions before the exam!

Task 2 is very simple. Here they are supposed to finish the start() method, which is very simple. The start() method should simply call, in order, rules() and doGuesses(). Once again, the students might wonder if they are done and have succeeded. This time it is simpler, if they have succeeded, the Guesser.java file not only compiles, it should be possible to write a test which creates a Guesser object and invokes start(). The whole game should start but since the getReply() method isn’t implemented yet, the game logic will be flawed (do guesses will interpret anything the user inputs as F (false). This last part may be the source of some confusion: How can the game compile and run, but still not work the way it is planned?

Task 3 is probably the most challenging part. Here, the student should complete the getReply() method so that it reads a T or an F from the user (and keeps asking for valid input until the user manages to actually answer T or F!). There are a few parts of this method which we predict will be hard for the students. First, it is to understand the role and function of the method. Be prepared that not all students immediately will understand this. The method’s role is to encapsulate user input and validation, and return input only when the user has provided valid input. Historically, we have seen that students have a hard time to grasp the concept of a method “returning” a value. Many students are used to see methods which only print stuff to standard out, and think that that is the normal way of “returning”. We have tried hard to better explain the difference between a side effect such as printing and a proper return statement that returns a value from some expression. Now, the getReply() method is tricky, because it does many things (the students may think). It reads input into a variable, but if the input is not valid, it also writes an error message back to the user. Make sure the students understand that these are just steps on the way to get a valid value (a “T” or an “F”) from the user. Only when this is accomplished, the method returns that value back to the caller.

The other thing which probably will be hard for some students with this method, is the logic for continuously asking for a value if the value last given is not valid.

We have provided some code examples and pseudocode to help with this logic. We suggest the following simple logic:

  • read input into variable
  • while input is invalid (input is not “T” and input is not “F”) // whatabout null?
    • write error messge
    • read input again into the variable
  • return the variable

Here, the students will be faced with a number of challenges. Do they understand the logic behind our suggested pseudocode? Are they comfortable with the syntax and semantics of the while-loop? Do they understand how to check validity, that is, to check that invalid input means that the variable is not “T” AND the variable is not “F”? Do they know how to handle null in relation to the equals() method? Do they understand that the user may produce a null value (by pressing Ctrl-D)? We still think that this method is good to have even in the first assignment. It shows the students the importance of reviewing previous chapters and section (while, methods, return-statement, null, equals) yet it is not extremely hard to write. But be generous to the students during supervision and facetime! This assignment is also meant to give the students a feeling of accomplishment, not a do-or-die assignment meant to flunk the weaker students.

To test that the getReply() method works, the students need to run start() on a Guesser object, which in turn will run rules() and doGuesses(). It is doGuesses() which uses (calls) the getReply() method. Encourage the students to run it like this and try with various invalid input (replies other than “T” or “F”). Encourage the students to play around with null and equals, both null as a literal value and null produced by readLine() in combination with Ctrl-D.

Task 4 is probably quite straight forward. Once the students have managed to get a fully functional Guesser class, they should write a game program in a stand-alone class (with only a main method to begin with). The main method of the program should simply create a Guesser object with the parameters (0, 1000). Then invoke the start() method and let the game begin. This time, the class they are writing should be called simply GuessingGame.

We don’t think this task will be hard for any of the students. The only challenge, as we see it, is to get the signature of the main method right, make sure the class name corresponds with the file name, and that the classpath allows the program to use Guesser (if they are not put in explicit packages, and are in the same directory this wouldn’t be a problem).

Task 5 is a continuation of the task 4. Now, the students should enhance their program so that the user running the program provides the values for low and high (the interval of the possible numbers, and the arguments to the constuctor). This is a recap from some of the exercises using the args array in main.

The things we predict will be a problem here, is to understand that Integer.parseInt will throw an exception if it cannot parse the argument. The students are not required to handle this in the mandatory parts, but the optional more challenging tasks include fixing this weakness. Another thing which is bound to happen, is that the students run the program with 0 or 1 arguments, and get an array indexing exception (ArrayIndexOutOfBounds). This will also be dealt with in the optional more challenging tasks. If the students encounter any of this, explain to them what happened and encourage them to do at least the first few optional tasks, or show them how to give valid arguments.

What the assignment is about

This first assignment is about writing a simple interactive game. We have provided most of the code, but there are missing parts we need you to complete and write for us.

Description of the game

Your task is to complete code for a game where the player thinks of a number in some interval, e.g. a number between 0 and 1000 inclusive. The game works like this: The player thinks of a number in the given interval, and the computer tries to guess what number the player is thinking of. The computer will try to use as few guesses as possible. In order to make good guesses, the computer asks the player questions about its guesses.

If the player is thinking of the number 333, the computer might start with a guess of 500, but ask the player “Is your number less than or equal to 500?”. The player must then answer truthfully by entering “T” for true (it is true that 333 is less than or equal to 500) or “F” for false.

When the computer is certain to have the correct guess, it will simply notify the user by outputting something like this: “You were thinking about 333 (took me 10 guesses)”.

The game software consists of a class Guesser which does all the job of talking to the player and making the questions until it finds the number. But the Guesser class is not complete and you need to write the missing parts for it to be complete. When the class is complete, you need to write a small class with a main method (a program) which creates a Guesser instance and starts the game.

Therefor, the first part of the assignment is to complete the code for the Guesser class, and the second part is to write a small program (a Java class with a main method) which uses the Guesser class to create and use a Guesser object.

Example run of the game

Here’s an example run where the player is thinking of 333:

Think of a number between 0 and 1000
I'm going to ask a few questions in order to guess the number.
Please answer T for true, and F for false
Is the number less than or equal to 500?
T
Is the number less than or equal to 250?
F
Is the number less than or equal to 375?
T
Is the number less than or equal to 313?
F
Is the number less than or equal to 344?
T
Is the number less than or equal to 329?
F
Is the number less than or equal to 337?
T
Is the number less than or equal to 333?
T
Is the number less than or equal to 331?
F
Is the number less than or equal to 332?
F
You were thinking about 333 (took me 10 guesses)

API for the Guesser class

The Guesser class will have a very simple API. A public constructor which takes two arguments (lower bound and upper bound), and only one public instance method, start(). The start() method will start the game.

Internal logic of the class

The finished class will have the following logic: First an instance is created with the lower bound and upper bound passed to the instance via the constructor. The constructor saves these values in instance variables called low and high. Next, the user of the instance calls the method start(). This method does two things: First it calls a private helper method which prints out the rules for the game to standard out. Next, it calls the private doGuesses() method, which calculates the first question and guess. The doGuesses() method has in turn a private helper method for reading the reply (T or F) from the user. When the doGuesses() method is sure it has the correct number, it prints a message to standard out and the method exits (the game is finished). A guesser object

The top part of the object is the public API - the constructor and the start() method. The lower part is private helper methods (called from start() ).

Unfinished source code

You can download the source code for the assignment here. The source code is not finished, and it is your task to complete it into a fully functional application.

The first part of the assignment is to complete the Guesser class. Later you will write a small program which uses the Guesser class.

Task 1 - write a constructor for the class

The idea behind the design of the Guesser class is that users of the class can construct a guesser object using a constructor to which the user of the class passes a lower bound and an upper bound for the interval of the possible numbers where the Guesser object will try to find the number the player of the game is thinking of.

The class declares two int instance variables, low and high, where the Guesser object will keep track of the initial lower and upper bound. If users of this class wants a game where the legal numbers are between 0 and 1000, the constructor should accept these two values and store them in low and high, respectively.

Look at the source code file (Guesser.java) for a comment indicating where you should write this constructor.

When you have written this constructor, you should be able to write test code to verify that the constructor exists. You can do this in a new class for the actual game program. We suggest that you create a class called GuessingGame (in a file called GuessingGame.java) with just a main method (public static void main(String[] args){ ... some code ...} ).

In the main method of your new class, verify that you can create a Guesser instance by calling the constructor with the arguments 0 and 1000:

 Guesser guesser = new Guesser(0, 1000);

If the GuessingGame.java compiles, then you know that your call to the constructors are valid (at least according to the compiler, which is an important first step - it is good to compile often, so that you quickly spot any syntactic errors).

Hints for Task 1

If you don’t remember how to write code for a constructor, please revisit the chapter Classes and in particular the section about defining constructors.

Remember that if the parameters (formal arguments) to the constructor have the same name as some instance variables, you can use the operator this to access the instance variable. For instance, this.low would signify the instance variable low. We recommend that you always use the operator “this” when accessing instance variables (inside the class where they are declared!), to be extra clear.

Stuff to think about (for Task 1)

The parameters to the constructor should be used for the initial interval of possible numbers. What would happen if a caller of the constructor swapped the order of the arguments? Would the code work for an interval of 1000...0 ?

Whose responsibility should it be to check that the parameters are valid? The caller’s or the constructor’s?

One way for a constructor (or method) to reject arguments, is to throw an IllegalArgumentException if the parameters are not meaningful. To reject invalid arguments for the constructor (in the case low is not less than high), you may use a statement such as this:

throw new IllegalArgumentException("The first parameter must be lower than the second parameter");

You would have to check whether it is the case first, of course, using a simple if statement.

In this lab, it is enough that the caller checks the validity of the arguments, so you don’t have to check this in the constructor of Guesser. You don’t have to do the check in the constructor if you don’t want to.

How do we know the constructor works? One way to test that the constructor not only compiles, but also saves the arguments for low and high, is to add a toString() method to the class. The toString() method could return a (reference to a) String which contains high and low and look like this:

public String toString(){
  return "low: " + low + " high: " + high;
}

Then create a test class called GuesserTest (in GuesserTest.java) with a main method which only creates an instance (or several) of Guesser and passes different values to the constructor and after each such creation prints the object by passing a reference to it to System.out.println() and you could verify that the output matches what you expected. Such a test could help using similar techniques for the other methods you are about to complete later in the assignment. Write separate tests for the separate methods you want to test.

A different test approach for the GuesserTest (if you choose to write a test at all) is to test if the toString() method returns what you expected using code:

if(p1.toString().equals("low: 0 high: 1000")){
  System.out.println("Test with 0, 1000 passed!");
}else{
  System.err.println("Test with 0, 1000 failed!");
}

Or, you can use our test program here to test that the constructor works.

Task 2 - complete the start() method

If you look at the source code in Guesser.java you will also see a comment indicating that you should complete the now empty start() method. This task should be really simple. You need only two statements to complete the start() method; first a call to the rules() method, then a call to the doGuesses() method.

Hints (for Task 2)

If you don’t remember how to call a method, please review the Objects in Java chapter. You need to look at the rules() and doGuesses() methods, in order to see if you need any arguments in order to call them.

Stuff to think about (for Task 2)

It is common for methods to call other methods. The start() method serves as a simple API for users of the Guesser class. The idea with this method is that it should be easy to use an object of class Guesser - create the object (instance) using the constructor and pass along the lower and upper bound for valid numbers. Next, just call start() without any arguments and let the game begin.

To start the game, the object needs to do two things. First show the rules to the player. This is done in a separate, private helper method called rules(). It should be called as the first instruction in start(). Next, it starts to do the guessing, which is done by calling a second private helper method called doGuesses(). Both rules() and doGuesses() are hidden from users of objects of this class, since they are private. The call to doGuesses() should be the second and last thing that happens in the start() method.

When doGuesses() is called from start(), control is passed to this method. Inside doGuesses(), values to ask the player questions about are calculated and printed to standard out. In order to get a reply back from the player (the player is supposed to type in T or F), doGuesses() uses a private helper method (which is not fully implemented but with your help it will be, in a task later on) called getReply() which reads a String from the console, and if the String is one of “T” or “F”, this String is passed back to doGuesses() as a String reference.

When doGuesses() has found the correct number, doGuesses() is finished and the JVM returns control to the method start() again. The start() method, however, has no more statements, so it too returns and control goes back to the code which called start() - in this case it will be the GuessingGame main method, which you will write in a later task (if you didn't write it for testing the constructor in Task 1).

Task 3 - finish the getReply() method

This method is also incomplete and you must implement it. The purpose of getReply() is to handle the input from the console (the player uses the keyboard to give answers), check that the input is valid and return the valid input to the caller (the caller is the doGuesses() method but that does not matter when you write the method).

Hints (for Task 3)

To read a String from the console, you may use the following code (in Windows CMD, Mac OS and GNU/Linux):

System.console().readLine()

That call reads a line of text and returns it. So it can be used as an expression of type “reference to String”. This in turn, means that you can use the call in an assignment, for instance as in this snippet (this is just an example - again for Windows CMD, Mac OS and GNU/Linux):

 String name;
 System.out.println("What is your name?");
 name = System.console().readLine();

If you are running Eclipse or cygwin, there is no System.console unfortunately. So you'll have to use something else, like a Scanner object.

Here's the code for using a Scanner which reads from System.in:

  String name;
  System.out.println("What is your name?");
  name = new Scanner(System.in).nextLine();

After the code in the example above (if it occurred in some program), the variable name would be assigned a reference to a String with whatever text the user entered.

Now, the getReply() method should only accept an answer of “T” or “F”. Anything else typed in by the player must be rejected with a message and then the player should be given a new chance to enter a reply again.

One way to continuously ask for new input while the player insists on giving invalid input, is to use a while loop.

The strategy could be as follows: First read the input into the variable reply. While reply is not equal to “T” and not equal to “F”, write “Please answer T or F!” and read new input into reply.

Pseudo code would be:

read line into reply; 
while( not reply equals T and not reply equals F){
  write please answer T or F;
  read line into reply;
}
return reply;

Why does this work? First you read a line from the player into the variable reply. Next you enter a while loop, only if reply does not equal “T” and does not equal “F” either. This means, that if the player entered a valid reply the first time, the while loop will not execute at all (the condition is false). If, on the other hand, the player starts by entering something illegal such as “I am Groot”, the condition for the while loop will be true because “I am Groot” is not equal to “T” and it is also not equal to “F”. In the while loop, the error message is shown, and reply is assigned a new value using the console-readline call. Only when the player learns how to answer correctly, the condition becomes false, and the while loop exits. Then it is safe to return reply.

How do we check Strings for equality? We have shown you the equals method work on Strings in several chapters and examples, and you can read about it in the online API documentation for java.lang.String. But here is a short recap, using code examples:

String s = "";
if(s.equals("")){
  System.out.println("s is empty");
}
s = "Adam";
if( !s.equals("Eve") ){
  System.out.println("s is not equal to Eve");
}else if( s.equals("Adam") ){
  System.out.println("s is, however, equal to Adam");
}else{
  System.out.println("We know that s is not equal to Eve"+
                     " and s is not equal to Adam");
}

Here we'll show you a method which works in all cases below (showing you the syntax for both System.console() and Scanner):

  private String readLine(){
    // If there is no System console, use a scanner
    if(System.console() == null){
      return new java.util.Scanner(System.in).nextLine();
    }else{
      // there was a System console, so we can use it
      return System.console().readLine();
    }
  }

You don't need to use that method. It exists here to show you that you can write code that would work with a System.console() if it exists, and with a Scanner(System.in) if there is no System.console().

Stuff to think about (for Task 3)

What about null? What happens if we have a String reference which is null (not referencing any object at all) and we use the reference to check equality with some String literal:

String s = null;
if(s.equals("Barcelona")){
  System.out.println("Ay, caramba");
}

To find out what happens, create a small test class with a main method and put the above inside the body of the main. For the impatient, we can show what happens here (but we recommend that you do the experiment yourself) when the program executes:

Exception in thread "main" java.lang.NullPointerException
	at YourClass.main(YourClass.java:9)

You cannot use a null reference (any reference variable with the value null) to call an instance method or access an instance variable for the simple reason that there is no instance referred to by the variable!

What does this mean in terms of testing for equality? It means that in order to create a robust program, you should always check for null references before calling equals on a String! However, this makes the while or if condition quite long:

 if(s!=null && s.equals("Barcelona") ){
   // ...
 }

There is an idiom to work around this problem. It uses the fact that you can use null as an argument to equals (it will return false). And you can call equals on String literals! So a more compact (but equally robust) way of formulating the same test and handle null is to write:

 if("Barcelona".equals(s)){
   System.out.println("Ay, caramba");
 }

But, would System.console().readLine() ever return null? Surely, the user cannot enter a null using the keyboard? If the user enters null using the keyboard, that would be interpreted as a java String with the value "null", right? Right, but the user can actually send something that will be read as null, by pressing Ctrl-d (the Control key and D simultaneously). This means, that if you want your program to be robust, you must use one of the idioms above to protect against null, if the input comes from System.console().readLine() .

Task 4 - Write a program using the Guesser class

This is the second part of the assignment, and here you will have to write all code from scratch. It is now time for you to write your own small program using the Guesser class you have completed in the previous steps.

Verify that you now have a constructor as described in task 1, that you have completed the start() method as described in task 2, and, that you have completed the getReply() method as described in task 3. You should now be able to write the actual game program. Create a class called GuessingGame which only has a standard main method. The class should be defined in a file called GuessingGame.java .

(You might have started this class in Task 1 if you chose to write it for testing the constructor - if so, just use that class)

The code for the main method should contain two simple steps:

  1. Create a Guesser reference variable and assign it a (reference to a ) new Guesser object, passing the values 0 and 1000 to the constructor.
  2. Use the Guesser reference variable to call the start() method

Compile and run your game and verify that it works. If it doesn’t work, consult with your classmates and the supervisors and hunt the problem down and fix it.

Task 5 - let the player choose the interval

In the previous task, the version of your GuessingGame program always uses the interval 0...1000. What if we let the user (the player) decide what interval to use? The constructor is created in such a way that the caller of the constructor provides the lower bound and the upper bound of the interval. This allows for flexibility, such as letting the player choose the interval. Your task is to change your GuessingGame program to accept two arguments from the command line, lower and upper bound. The program should be run like this:

$ java GuessingGame 0 10000

if the player wants to have an interval between 0 and 10 000.

In order to accomplish this, you must use the args array of the main method, and convert the two first elements to int values, and use those values as arguments to the constructor of Guesser.

Hints (for Task 5)

The args array in the signature of the main method is an array of String references. The first argument (if any) will end up in args[0], the second argument (if any) will end up in args[1] etc. But the constructor in the Guesser class wants two values of type int. In the example command line above, the first argument will be "0" (of type String) and the second argument "10000" (also as a String). So, how do we convert the String "0" to an int of value 0? The class java.lang.Integer has a helpful static method we can use for exactly this purpose, as shown by the following example code:

 String zero = "0";
 int z = Integer.parseInt(zero);

Note that if the String represented by the argument to parseInt is not possible to convert into an int, there will be an exception and if we don’t handle that, the program will crash with a report similar to:

Exception in thread "main" java.lang.NumberFormatException: For input string: "notAnInt"

The error message above means that there was a call to Integer.parseInt() with an argument of a String representing the text "notAnInt" and that String is of course hard to interpret as an integer value. Only String literals which contain characters representing actual integer values are possible for parseInt() to successfully convert to the corresponding int value.

Now you know how to convert an argument of type String, like "0" or "10000" to the type int, so now you should have the tools for completing the Task 5. You can as the first step of the main method, create two int variables and assign them the values you get from using parseInt() with args[0] and args[1] as values. Then you can use those variables as arguments to the constructor of Guesser.

Optional extra challenges for Assignment 1

If you feel that you need more challenges, you will find some optional voluntary extra tasks here.

Optional task 1 - Check arguments

Make sure that your program has the two required arguments for low and high, and write a usage message otherwise. A usage message is a text explaining how a command or program is supposed to be invoked from the command line. Here’s an example run where the user doesn’t provide the two mandatory arguments:

$ java GuessingGame
Usage: java GuessingGame low high
 where low is an integer for the lower bound
 and high is an integer for the upper bound
 of the interval for the numbers of the GuessingGame

Having this check and usage message makes your program more self explanatory. If the user who wants to play forgets to provide the lower and upper bound for the valid numbers, the program exits with a message explaining how to start it.

Hints (for optional task 1)

To check the number of arguments to a program, you use the length variable in the args array. You have used this in some of the exercises to the chapter Objects in Java. Review this chapter if you don’t remember how to check the number of arguments.

The usage method should only be printed if the user fails to give the correct amount of arguments. This means that you should start with a check for this, and if the check shows that the number of arguments is wrong, you should print the usage message, and then exit the program. Use an if-statement to check if the number of arguments is wrong, if so do the usage printing and then call:

 System.exit(1);

Calling System.exit() with a number immediately terminates the program. So this needs to be inside the if-block, after the usage message printing, so that it only is executed when the arguments are wrong. Code after the if-statement will execute as normal if the condition for the if-statement is false (that is, the number of arguments were correct). Pseudo code:

 if number of arguments are not 2 {
   print usage message
   System.exit(1);
 }
 code to start the game...

Stuff to think about (for optional task 1)

The printing of the usage message consist of several lines of text. It might be a good idea to move this code out to a method instead, and call this method inside the if-statement. But how do we call a method from the main method? Remember that the main method is a static method. That means that the main method runs independently from any particular instance of the class it is defined in. Calling a method in the same class from such a “static context” means that either do we need to create an instance of the class GuessingGame itself, and use that in order to call some instance method like usage(), or we need to call another static method. The usage() method doesn’t need any instance variables of the GuessingGame class in order to do its job - its job is simply to do some printing using System.out.println(). This means that the usage() method is a good candidate for a static method.

We suggest that you make usage() a private static void method of the GuessingGame class. It will only be used as a helper message to keep the main method cleaner and smaller. It is perfectly legal, then to call usage() from the static context in main, without the need of creating any objects.

The pseudo code from above would now read:

 if number of arguments are not 2 {
   usage();
   System.exit(1);
 }
 code to start the game...

The signature of the usage method would simply be:

 private static void usage()

Optional task 2 - handle the parsing of the arguments

As was said in task 5 of the mandatory part of the assignment, calling Integer.parseInt() will fail with an exception if the argument String to be parsed is not possible to convert into an int. The exception that was thrown by parseInt() was of class NumberFormatException. You may catch and handle that case using exception handling code. The chapter Exceptions is a good place to start if you need to refresh your memory about how to catch an exception of a named class like that.

Your task here is to add a try-catch block around the parsing of the arguments. The arguments should be parsed after the number of arguments check, of course, since there is no point in trying to parse arguments if there aren’t any.

Create two int variables called low and high. Then start a try-block ending with a catch-clause catching NumberFormatException. In the block of the catch clause, write an error message explaining that the arguments could not be interpreted as integer values, and explain to the user that both arguments must be numbers, then exit the program.

Hints (for Optional task 2)

You must declare your int variables outside the try-catch block if you plan to use them after the try-catch block, because if you declare them inside the block, they will be local variables to the block and not known outside the block.

Alternatively, you can declare them inside the block but then you must also use them inside the block. Here is pseudo code to explain the first alternative (declaring outside, using after):

 int low=0;
 int high=0;
 try{
  parse args...
 }catch(NumberFormatException nfe){
  write error message
  System.exit(2);
 }
 Guesser guesser = new Guesser(low, high);
 ....

Alternative 2, declaring and using inside block:

 try{
   int low  = parse int from args[0];
   int high = parse int from args[1];
   Guesser guesser = new Guesser(low, high);
   use guesser...
 }catch(NumberFormatException nfe){
  print error message and exit program
 }
 // No more code, main ends here

Use the alternative you find most intuitive.

Stuff to think about (for optional task 2)

Why did we give different arguments to System.exit in the example code? We wrote System.exit(1) for the case of the wrong number of arguments problem, and System.exit(2) for the number format exception problem. The reason is that we can use different numbers for different cases and write a manual explaining what different numbers mean.

When a program (following the posix standard) exits, it always has an exit code in form of an integer value. A program which exits normally should have the exit code 0. Any other value than 0 means that the program exited abnormally (there was a problem). It is always possible to check the exit code from a program from the shell. In bash, you can check the number like this:

 $ javac GuessingGame.java 
 $ echo $?
 0

(Normal termination, all went well, exit code was 0).

When something goes wrong, the exit code is not 0. Here’s the exit code from javac when there is a syntax error:

 $ javac GuessingGame.java 
 GuessingGame.java:3: error: ';' expected
     int low = 0
               ^
 1 error
 $ echo $?
 1

(Compilation error, exit code was 1)

When something else goes wrong, the exit code varies:

 $ javac Bogus.java
 javac: file not found: Bogus.java
 Usage: javac <options> <source files>
 use -help for a list of possible options
 $ echo $?
 2

(File not found, exit code was 2)

It is good practise to use different exit codes for different error situations (if you document it so that the user can look the codes up, it is even better of course!).

Optional task 3 - Use an object to parse and hold the arguments

This task is a little harder. The idea here is to remove all error handling and parsing from the main method, and use an object for this instead. The goal is to have an object which holds the two arguments low and high, and also knows if something went wrong and what happened.

The use of the helper object could make the main method small and look like this:

    IntervalParser range = new IntervalParser(args);
    if(! range.couldParse()){
      System.err.println(range.getErrorMessage());
      usage();
      System.exit(range.getErrorCode());
    }
    Guesser guesser = new Guesser(range.low(), range.high());
    guesser.start();

Example runs using this code:

$ java GuessingGame
You must provide two arguments
Usage: java GuessingGame low high
 where low is an integer for the lower bound
 and high is an integer for the upper bound
 of the interval for the numbers of the GuessingGame

$ java GuessingGame a b
The arguments must be numbers.
Usage: java GuessingGame low high
 where low is an integer for the lower bound
 and high is an integer for the upper bound
 of the interval for the numbers of the GuessingGame

$ java GuessingGame 3 1
The lower bound must be less than the upper bound.
Usage: java GuessingGame low high
 where low is an integer for the lower bound
 and high is an integer for the upper bound
 of the interval for the numbers of the GuessingGame

You still need the usage method as a private static method of the GuessingGame class.

Your task here, is to write the IntervalParser class. We have provided a skeleton for you here.

The work for you is in the constructor where all checks are carried out. If a check fails (or number format exception is thrown), you set the variable errorCode to something unique and the error String variable to an appropriate message (you may use the messages from the sample runs above).

Optional task 4 - understanding the guessing algorithm

The strategy used by the doGuesses() method in Guesser, is to ask as few questions as possible until there is only one possible number left which must be the number the player is thinking about. To use as few questions as possible, the method is using what is called a binary search. The idea is that the fastest way to find the number, is to start in the middle of the range, and throw away the half which cannot hold the number. Which half to throw away is determined by the answer to the question.

If the range is 0...1000 the half is 500. The method asks the player: is your number less than or equal to 500? If the answer is T (for true), then the upper half is thrown away (if it’s less than or equal to 500, it cannot be greater than 500!). Then a new middle is calculated (250). Every time, half of the numbers left are thrown away. This means that for the interval 0...1000 at most ten questions are ever needed, because, if you divide 1000 in half, you can only do so ten times before you reach 0 (in integer division, 1/2 is 0).

Another way of putting it, is that if you start with 1, and double it ten times, you arrive at 1024.

Yet another way to express that, is that 2^10 is 1024.

Now, your task is to figure out, how many questions (at most) would be needed to find a number between 0 and 100? Between 0 and 10 000? Between 0 and 100 000? Between 0 and 1000 000?

Hints (for Optional task 4)

The task is about finding out how many times you can divide the upper bound (if the lower bound is 0) before you reach zero. This number corresponds to the logarithm of the upper bound to base 2. The number of questions needed to find a number in 0..200 for instance, is realted to the logarithm of 200 bound to base 2. A less formal way to express that, is “What number should I raise 2 to the power of, to get 200?” It should answer the equation 2x = 200. If you have a calculator present, you can look up log2(200) which is ~ 7.64. That means that 27.64=200. Then we can say that we can divide 200 about 7.64 times, but we can’t deal with fractions here. Since, 7 times would be too little (27 < 27.64), we need to round up to 8. So the program would need 8 at most guesses.

If you are not into maths and logarithms, you can actually test your way to the answer. You can start with 1 and double it until you get a number equal to or greater than the upper bound.

You might be surprised how few times you need to double 1, to reach quite large numbers.

A manual test for finding the maximum number of questions needed for 200 would read:

1*2=2 1
2*2=4 2
4*2=8 3
8*2=16 4
16*2=32 5
32*2=64 6
64*2=128 7
128*2=256 8 (256 > 200, stop)

Answer: 8 questions.

You could also work your way down from 200 until you get 0:

200/2=100 1
100/2=50 2
50/2=25 3
25/2=12 4 (integer division)
12/2=6 5
6/2=3 6
3/2=1 7 (integer division)
1/2=0 8

Answer: 8 questions.

Assignment 2 - Address book application

Description

In this assignment, you will create an application for an address book. The application will have a text interface presenting the user with a menu. The features of the address book application will be:

  • List entries
  • Add entry
    • Adding a contact to the address book should be done interactively
    • Adding a contact to the address book should automatically save the contact book to file

The assignment will be a mix of using an API provided by the teachers and writing classes needed for the components of the address book. Using the API provided by the teachers, usually means completing some half-ready code, or in some cases, writing new classes from scratch.

This assignment consists of four main tasks, leading up to a functional application (in small increments). The first two steps/tasks come with test code to help you verify that you have completed one step and can move on to the next.

This application will also be used in the next assignment, assignment 3, where the students will refactor parts of the application and add features.

Topics covered in this assignment

  • error handling (exceptions) - exceptions are thrown and we have a global exception handler in the main method
  • inheritance - Contact extends Object and overrides equals, hashCode and toString
  • access modifiers - generally keeping instance variables private and only some of the instance methods public
  • implementing an interface
    • anonymous inner class for MenuActions
  • instance variables and methods - saving constructor parameters in instance variables, calling instance methods
  • static variables and methods (including constants) textutils class offers a static method
  • constructors - completing the constructor of the Contact class, writing a constructor in the SimpleApplication class from specification
    • test cases are provided with the source code the students get
  • packages/imports - There are a few packages in the application, which means both package declarations and import statements - also, understanding how to compile the application and running it requires understanding of class path and packages
  • implementing a class from a design - both completing existing code and (optionally) writing the SimpleApplication class from scratch
  • collections (using) - The SimpleAddressBook maintains an internal List<Contact>
  • generics (using) - The SimpleMutableList interface is generic and the SimpleAddressBook uses <Contact> as type parameter

Description of the application

This is a sample run of the finished application:

INFO: There is no address book file.
0 items loaded from file.

====Address book====

0 List
1 Add
2 quit
Please enter a number from the menu: 0  (user entered "0")

You selected List


====Address book====

0 List
1 Add
2 quit
Please enter a number from the menu: 1  (user entered "1")

You selected Add

Name: Ben Benson                        (user entered "Ben Benson")
Email: ben@benson.com                   (user entered "ben@benson.com")
Phone: 123456                           (user entered "123456")
Saving in /home/username/.address_book...

====Address book====

0 List
1 Add
2 quit
Please enter a number from the menu: 1  (user entered "1")

You selected Add

Name: Cyrus Cyber                       (user entered "Cyrus Cyber")
Email: cyrus@cyber.com                  (user entered "cyrus@cyber.com")
Phone: 654321                           (user entered "654321")
Saving in /home/username/.address_book...

====Address book====

0 List
1 Add
2 quit
Please enter a number from the menu: 0  (user entered "0")

You selected List

Ben Benson ben@benson.com 123456
Cyrus Cyber cyrus@cyber.com 654321

====Address book====

0 List
1 Add
2 quit
Please enter a number from the menu: 2  (user entered "2")

Bye!

Understanding the layout of the code provided

The zip file you will download contains a directory structure as show below:

.
|-- bin
|-- doc
|   |-- allclasses-frame.html
|   |-- allclasses-noframe.html
|   |-- constant-values.html
|   |-- deprecated-list.html
|   |-- help-doc.html
|   |-- index-all.html
|   |-- index.html
|   |-- org
|   |   `-- addressbook
|   |       |-- storage
|   |       |   |-- Contact.html
|   |       |   |-- package-frame.html
|   |       |   |-- package-summary.html
|   |       |   |-- package-tree.html
|   |       |   |-- SimpleAddressBook.html
|   |       |   `-- SimpleMutableList.html
|   |       `-- ui
|   |           `-- cli
|   |               `-- menu
|   |                   |-- MenuAction.html
|   |                   |-- MenuExample.html
|   |                   |-- Menu.html
|   |                   |-- package-frame.html
|   |                   |-- package-summary.html
|   |                   `-- package-tree.html
|   |-- overview-frame.html
|   |-- overview-summary.html
|   |-- overview-tree.html
|   |-- package-list
|   |-- resources
|   |   |-- background.gif
|   |   |-- tab.gif
|   |   |-- titlebar_end.gif
|   |   `-- titlebar.gif
|   |-- serialized-form.html
|   `-- stylesheet.css
`-- src
    `-- org
        `-- addressbook
            |-- main
            |   |-- SimpleApplication.java
            |   `-- SimpleMain.java
            |-- storage
            |   |-- Contact.java
            |   |-- SimpleAddressBook.java
            |   `-- SimpleMutableList.java
            |-- tests
            |   |-- TestContact.java
            |   `-- TestSimpleAddressBook.java
            |-- textutils
            |   |-- TextUtilsExample.java
            |   `-- TextUtils.java
            `-- ui
                `-- cli
                    `-- menu
                        |-- MenuAction.java
                        |-- MenuExample.java
                        `-- Menu.java

Wow, that’s a lot of files!

Let’s break this information down a little bit. The zip file will have two basic directories, bin and src. The src directory contains all source code for the application, distributed in packages (directories and sub directories). The bin directory will contain the class files (maintaining the same directory structure and packages as the src directory).

This means that in order to compile the source code, you need to tell javac to put the resulting class files in the bin directory structure. This is how you do that (on one single command line):

javac -cp src/ -d bin/ src/org/addressbook/tests/*.java src/org/addressbook/storage/*.java src/org/addressbook/main/*.java src/org/addressbook/ui/cli/menu/*.java src/org/addressbook/textutils/*.java

You are giving javac two options. First -cp src/ which tells javac that the packages are relative to the src directory. Second, -d bin/ which tells javac that the resulting class files should go to the bin/ directory structure.

This lets you compile the source code from the directory where both src and bin is located.

To compile only e.g. the org.addressbook.storage.Contact class, you can use the following command line:

$ javac -cp src/ -d bin/ src/org/addressbook/storage/Contact.java

To compile the test programs (which you should do right away!), you can use this command line:

$ javac -cp src/ -d bin/ src/org/addressbook/tests/*.java

To run the test file for testing the Contact class, you may use the following command line:

$ java -ea -cp bin/ org.addressbook.tests.TestContact

The flag -ea is very important, since it enables assertions! Assertions are the things which make tests fail when a condition is not fulfilled. Don’t forget the -ea flag! The flag -cp bin/ lets you run the program from the directory where both bin and src are located. It tells java that the package used is relative to the bin/ directory.

If you think that using class path like this is too complicated, we have provided a build.sh script for you, which you can use to compile all files:

$ ./build.sh

If you think running the tests (and remembering the -ea flag) is difficult, you may use the test scripts:

$ ./run_test_contact.sh
$ ./run_test_simple_address_book.sh

Getting started with assignment 2

Download the code

First download the zip file with all the code

Create a directory for your work and move the zip file there and unzip it.

Task 1. Finish the Contact class

Compile the Contact class and verify that it compiles.

$ javac -d bin src/org/addressbook/storage/Contact.java

Hint: you must be in the directory containing the directories bin, src, and doc. The flag -d bin tells javac to put the class file for Contact in the bin directory structure.

Task 1.1 Finish the constructor

Your first task is to finish the constructor (in the class Contact). The body of the constructor is empty as it is now. Your first task is to make sure that the constructor saves the parameters in appropriate instance variables. Hint: remember the keyword “this” to distinguish between parameters and instance variables that share the same name!

Before you start coding, run the tests for the Contact class. Running the test program requires that you add the flag -ea to the java command, as well as the flag -cp bin. The full compilation command line then becomes $ java -ea -cp bin org.addressbook.tests.TestContact

The tests should fail with the following message:

$ java -ea -cp bin org.addressbook.tests.TestContact
=====Testing constructor=====
Testing new Contact("Name Nameson", "email@email.com", "123")
Exception in thread "main" java.lang.AssertionError: * Name wasn't saved!
	at org.addressbook.tests.TestContact.testConstructor(TestContact.java:14)
	at org.addressbook.tests.TestContact.main(TestContact.java:65)

The test should fail! You haven’t fixed the constructor to save the parameters yet! Write the code that saves the parameters to the instance variables, and run the test again. This time, the test should be successful if you have written the code correctly. Verify that the first test passes by looking at the output from the test and verify that it says:

=====Testing constructor=====
Testing new Contact("Charlie Ceeson", "charlie@email.com", "12345")
* Constructor test for contact: Charlie Ceeson charlie@email.com 12345 passed.

Don’t worry if the next test fails. If the constructor test doesn’t pass, try to fix your code, compile and run the test until the test passes with the message above. Don't forget to save your source code between each fix, and don't forget to re-compile it before running the test again. The command line to compile the Contact class was:

$ javac -d bin src/org/addressbook/storage/Contact.java

The test verifies that the constructor has saved the arguments to the instance variables, by calling the accessor methods name(), email() and phone() on a newly created Contact object.

Task 1.2 Make the constructor smarter

Next, you should make your constructor in Contact a little smarter and safer. We want Contact objects which have at least a name! Email and phone may be empty, but not the name.

We don’t want to accept a null value for the name parameter. Write an if-statement such that if name is null, a NullPointerException should be thrown with a message explaining that name cannot be null.

Also, we do not want to accept an empty String for the name parameter. Add an if-statement which checks if name equals the empty String. Hint: use the equals() method! The empty String is two double quotes with nothing inside them. If the name parameter is the empty String, throw an IllegalArgumentException with a message explaining that the name parameter cannot be the empty String. Finally, we do accept null parameters for the email and phone parameters, but they should be saved as the empty String in the instance variables, if they are null.

Add an if-statement which checks if email is null. If so, save the empty String in the email instance variable, otherwise save the parameter email in the instance variable. Do the same for the phone parameter and instance variable.

Hint: To distinguish between a parameter and an instance variable with the same name, use the keyword this. If the parameter is called email, then you can use this.email to specify the instance variable with the same name.

To check if you have succeeded, run the test again and verify that you get this result:

=====Testing constructor=====
Testing new Contact("Charlie Ceeson", "charlie@email.com", "12345")
* Constructor test for contact: Charlie Ceeson charlie@email.com 12345 passed.
=====Testing constructor with null checks=====
* Constructor null tests passed.

If the tests in the org.addressbook.tests.TestContact test program passed, you have succeded in this part of the assignment! The Contact class’ constructor works as required.

Task 1.3 Finish the compareTo() method

In order for a list of contacts to be sortable, we have decided that Contact should implement the Comparable<Contact> interface. This is a small interface which only declares one method, public int compareTo(Contact other) . The only part of a contact that should be considered when comparing two Contacts is the name (which is a simple String reference). Now, the good news is that Strings are Comparable, so we only have to do two things in the method body for compareTo:

  1. Check if the parameter other is null, in which case we throw a NullPointerException
  2. return the result of using compareTo between this object’s name and other’s name

Run the tests for Contact again and verify that you have passed the tests for compareTo().

Task 1.4 Finish the equals() method

We have decided that it is useful to be able to check if two contacts are to be considered equal using the equals() method inherited from Object. In order for this to work, we need to override the inherited version of this method. Don’t worry about this step, because as with compareTo, the only part of a Contact to consider when deciding whether two objects are equal is, again, the name (for simplicity). And, again, we can take advantage of the String class’ implementation, since it has a functional equals() method we can use.

There is, however a contract for the equals() method, which is that two objects who are equal (according to the equals() method) must also return the same hashCode(). We have implemented the hashCode() method for you. You can read more about equals() and hashCode() here.

There are a few steps to follow for you to implement the equals() method as listed below:

  1. Check if parameter other is null, in which case you should return false
  2. Check that parameter other is an instanceof Contact - if it isn’t return false
  3. Create a local reference variable of type reference to Contact and assign it the other parameter cast to a reference to Contact
  4. Return the result of the local variable’s name equals this object’s name

Run the tests again and verify that the equals test also passes.

A complete successful test run for Contact should yield the following output:

=====Testing constructor=====
Testing new Contact("Name Nameson", "email@email.com", "123")
* Constructor test for contact: Name Nameson email@email.com 123 passed.
=====Testing constructor=====
Testing new Contact("Bob Bobson", "bob@email.com", "1234")
* Constructor test for contact: Bob Bobson bob@email.com 1234 passed.
=====Testing constructor=====
Testing new Contact("Charlie Ceeson", "charlie@email.com", "12345")
* Constructor test for contact: Charlie Ceeson charlie@email.com 12345 passed.
=====Testing constructor with null checks=====
* Constructor null tests passed.
=====Testing compareTo=====
* Test of compareTo() in Contact passed!
=====Testing equals=====
* equals() test for Contact passed.

Task 2. Finishing the SimpleAddressBook class

The idea here is that the Main class, the actual starting point of your program, can create and start the objects it needs. That would be a Menu, where the different menu options work on a SimpleAddressBook object!

Before we try to tie everyting together, we’d better make sure that the actual SimpleAddressBook object (which keeps a list of Contacts) is also working.

What signifies a SimpleAddressBook? In our case, the requirements are that the SimpleAddressBook should be able to respond to the following simple requests for actions:

  1. adding a Contact
  2. retrieving the number of contacts currently in the address book
  3. listing all Contact:s to the stdout
  4. saving the current address book to some place
  5. loading a previous address book from some place
  6. indicating whether a specified contact is already in the address book (not used in this lab, but in the next)

Actually, these actions correspond to the methods declared in the SimpleMutableList interface we have provided:

public interface SimpleMutableList<E>{

  public int numberOfEntries();
  
  public void listEntries();
  
  public void addEntry(E entry);

  public void save();
  
  public void load();

  public boolean contains(E entry);

}

Since SimpleAddressBook implements the SimpleMutableList interface, we know that SimpleAddressBook has implemented all methods above. One of the methods, however, is not fully functional yet. It is your task to finish it.

Task 2.1 - compile SimpleAddressBook

Start by confirming that you can compile the SimpleAddressBook class:

$ cd src/
$ javac -d ../bin/ org/addressbook/storage/SimpleAddressBook.java
Task 2.2 finish the add() method

Run the tests for SimpleAddressBook. Don’t worry that the tests fail, it is expected:

$ java -cp bin -ea org.addressbook.tests.TestSimpleAddressBook 
Test: adding Arnold Llloyd a@test.com 12345
You must implement addEntry(Contact c)
In class org.addressbook.storage.SimpleAddressBook
* Adding null null null
Exception in thread "main" java.lang.AssertionError: number of entries should increase by one after add!
	at org.addressbook.tests.TestSimpleAddressBook.testAdd(TestSimpleAddressBook.java:22)
	at org.addressbook.tests.TestSimpleAddressBook.main(TestSimpleAddressBook.java:30)

The tests fail with a complaint about you having to implement add()! So let’s do that. Here’s the single simple step to implement the add method:

  • Add a statement which adds the parameter c to the internal list called entries

Explanation: The SimpleAddressBook class maintains an internal List<Contact> in an instance variable called entries. This means that the add(Contact c) method simply stores the parameter c in this internal list, using the method add(E) from the java.util.List interface. Check the online API docs to verify that the java.util.List interface has an add() method if you don’t believe us! In fact, check the online API docs, anyway, since it is good practice.

Run the tests again and verify that the first test now passes:

$ java -cp bin -ea org.addressbook.tests.TestSimpleAddressBook 
Test: adding Arnold Llloyd a@test.com 12345
* Adding Arnold Llloyd a@test.com 12345
* Number of entries was increased by one
* list.contains(c) returns true
* Test with Arnold Llloyd a@test.com 12345 passed.
Test: adding Bernard Blake b@test.com 234354
* Adding Bernard Blake b@test.com 234354
* Number of entries was increased by one
* list.contains(c) returns true
* Test with Bernard Blake b@test.com 234354 passed.
Test: adding Cecil B. Demented c@test.com 234234
* Adding Cecil B. Demented c@test.com 234234
* Number of entries was increased by one
* list.contains(c) returns true
* Test with Cecil B. Demented c@test.com 234234 passed.

That’s actually all you had to do in order to complete the SimpleAddressBook class.

Task 3 - Create the SimpleApplication class

Our suggested design for the complete application is that you create a class, SimpleApplication, which is responsible for creating a menu with options for the functionality of the application and a way to start the application.

The main class (which you haven't written yet) will create an object of type SimpleApplication and call the start() method on the SimpleApplication object. The SimpleApplication object then starts the menu object which displays the menu options to the user and handles the user’s choice.

The SimpleApplication class should create a menu with three menu options:

0. List
1. Add
2. Quit

It is your task to populate the menu object in SimpleApplication with these menu items. See the MenuExample.java file for an example on how to create a menu item with menu options as text and a MenuAction connected to each option.

Do you have to write this class from scratch? You can choose to if you want, or use a simple path as explained below.

Task 3 - simple version

Use the file src/org/addressbook/main/SimpleApplication.java and complete only the createMenu() method.

The createMenu() method should add two menu items to the menu (the "Quit" menu item is added automatically by the menu object!).

The first item should have the text "List" and a MenuAction which in its onItemSelected() method calls listEntries() on the address book (the SimpleApplication class maintains a SimpleAddressBook in a variable called list!).

The second item should have the text "Add" and a MenuAction which in its onItemSelected() method reads a name, an email and a phone number from the user, then creates a new Contact using this information and calls addEntry(), with this Contact as the only argument, on the address book. As the last statement in the onItemSelected() method, save() should be called on the address book (so that the new entry is saved in the address book file as well).

The "Add" MenuAction is the harder-to-write of the two. Let's explain how it should work again! It should present the user with a prompt for the name of the new contact and save it in a variable, then prompt the user for the email and save that in a variable and finally prompt the user for the phone number and save that in a variable. After getting the name, email and phone from the user, it should create a new Contact object, and call addEntry() on the list variable with the new Contact reference variable as the argument.

How can you ask the user for a name and save the result in a variable? You may write your own implementation in order to do this, but you can also use a utility method we have provided for you. See org.addressbook.textutils.TextUtilsExample to see how to ask the user a question and get a String reference back.

How do you know that your code works? Well, you need to complete Task 4 and create and run the SimpleApplication from the main method. You'll create the class with the main method in the next task, but first we'll give you the harder version of this subtask which is optional.

Task 3 - challenge version (optional and harder)

If you want something more challenging, you are free to create the SimpleApplication class from scratch. Start by removing (or moving) the SimpleApplication.java file we have provided for you, and start writing your own version.

Create the class org.addressbook.main.SimpleApplication (it should be declared in a file: src/org/addressbook/main/SimpleApplication.java and have a package declaration declaring it to be part of the package org.addressbook.main).

The class should have the following components:

  • A private instance variable of type reference to SimpleMutableList<Contact>
    Hint: you need to import the following classes:
org.addressbook.storage.Contact
org.addressbook.storage.SimpleMutableList
org.addressbook.storage.SimpleAddressBook
org.addressbook.ui.cli.menu.Menu
org.addressbook.ui.cli.menu.MenuAction
  • A private instance variable of type reference to Menu which should be initialized to a new Menu object with a suitable title for the menu, e.g. “Address Book”
  • A public constructor which takes no arguments, but performs the following tasks:
    • initializes the SimpleMutableList variable to a new SimpleAddressBook()
    • calls the load() method of the address book
    • prints a diagnostic message indicating how many Contacts was loaded
  • A private method createMenu() which adds two menu items to the menu (the “Quit” menu item is added automatically by the menu)
    • The first item should have the text “List” and a MenuAction which in its onItemSelected() method calls listEntries() on the address book
    • The second item should have the text “Add” and a MenuAction which in its onItemSelected() method reads a name, an email and a phone number from the user, creates a new Contact using this information and calls addEntry with this Contact as the only argument on the address book. As the last statement in the onItemSelected() method, save() should be called on the address book.
  • A public method start() which calls, in turn, createMenu() in this object and start() on the menu variable.

This is a rough outline of the class SimpleApplication:

package declaration...
import statements...
public class SimpleApplication{
  declare the SimpleMutableList<Contact> variable (call it e.g. list)
  declare the Menu variable (call it e.g. menu) and
        assign it a new Menu object with the argument "Address book"
  declare the public constructor with no arguments{
     list is assigned a new AddressBook object
     call load() on the list
     print how many contacts were loaded (use the numberOfEntries() 
           method for the list variable) 
  }
  declare a private void method createMenu(){
     use the menu variable and call addMenuItem with a text of
         "List" as the first argument and an anonymous inner class
         of MenuAction overriding public void onItemSelected()
         which calls listEntries() on the list variable
     use the menu variable and call addMenuItem with a text of
         "Add" as the first argument and an anonymous inner class
         of MenuAction overriding public void onItemSelected()
         which reads name, email and phone from the user (and saves
         the result in three variables), and creates a new Contact 
         using the three variables.
         Call addEntry() on the list variable with the new contact 
         reference as the argument.
         Call save() on the list variable.
  }
  declare a public void method start(){
    call createMenu()
    call start() on the menu variable
  }
}

How can you ask the user for a name and save the result in a variable? You may write your own implementation in order to do this, but you can also use a utility method we have provided for you. See org.addressbook.textutils.TextUtilsExample to see how to ask the user a question and get a String reference back.

How do you know that your code works? Well, you need to complete Task 4 and create and run the SimpleApplication from the main method.

Task 4 - write the main class which starts the application

Task 4.1 write a simple main method which starts the application

Your final task is to write the class with the main method which kicks off (starts) the application. Write the class org.addressbook.main.SimpleMain (in the file src/org/addressbook/main/SimpleMain.java). This class needs a package declaration, declaring that it is part of the org.addressbook.main package, and a standard main method.

The main method should declare a local variable of type reference to SimpleApplication and assign it a new SimpleApplication. Run start() on this variable and verify that the application starts (by showing the user the menu alternatives).

Task 4.2 - add a global exception handler to the main method

Put a try-catch block around the code in main. Catch any Exception and print an error message to System.err directing the user to read the log file for more information.

Why do we want to do this? If there is an uncaught exception in the code, it will bubble up all the way to the main method. We’d like to spare the user from any Java-specific error messages like NullPointerException or stack traces, so we present an error message instead and refer the user to the log file for such technical information.

How does this work? If there is a failure (an exception is thrown) for instance in the file handling code for loading the address book from the file system, the load() method catches this exception and logs the stack trace to the log file, before re-throwing an unchecked runtime exception which will bubble up all the way to main. Why do we want this to propagate all the way to main? Because if there is a problem reading the saved address book from the file system, there is really nothing we can do about it but to gracefully shut down the application and log the error. There is no way we can recover from this error.

The outline of this final version of the main method now becomes:

public static void main(String[] args){
  try{
    create simple application object and call start()
  }catch(Exception e){
    write error message to standard error referring the user to the log file
  }
}

Hint #01: Look at the class org/addressbook/storage/SimpleAddresBook for information about the log file's location.

Hint #02: For Windows users the home directory path follows the Windows home variable, typically C:\Users\UserName where UserName is your user name (e g Ada Lovelace). It is not the cygwin home directory.

Testing the main application (and indirectly the SimpleApplication class)

You have to run the main class in order to test if your SimpleApplication’s menu items work. We suggest the following procedure to test your application:

Start by running the application and select the "List" menu item. It should list nothing, since it doesn’t yet contain any Contacts:

$ java -cp bin/ org.addressbook.main.SimpleMain
INFO: There is no address book file.
0 items loaded from file.

====Address book====

0 List
1 Add
2 quit
Please enter a number from the menu: 0        <---- user enters "0"

You selected List


====Address book====

0 List
1 Add
2 quit
Please enter a number from the menu: 2        <---- user enters "2"

Bye!
$

Note that the applications notifies you that there are no entries loaded, and when you select the number for listing the contacts, nothing is printed (since there are no contacts).

Next, run the program again and add a few contacts:

$ java -cp bin/ org.addressbook.main.SimpleMain
INFO: There is no address book file.
0 items loaded from file.

====Address book====

0 List
1 Add
2 quit
Please enter a number from the menu: 1        <---- user enters "1"

You selected Add

Name: Ben Afflec
Email: ben@hollywood.com
Phone: 123456
Saving in /home/USERNAME/.address_book...

====Address book====

0 List
1 Add
2 quit
Please enter a number from the menu: 1        <---- user enters "1"

You selected Add

Name: Adam Axelson
Email: adam@name.com
Phone: 654321
Saving in /home/USERNAME/.address_book...

====Address book====

0 List
1 Add
2 quit
Please enter a number from the menu: 2        <---- user enters "2"

Bye!

Next, run the application again, to verify that it finds the address book file (in your home directory, it should have been created for you) and try to list the contacts:

$ java -cp bin/ org.addressbook.main.SimpleMain
2 items loaded from file.

====Address book====

0 List
1 Add
2 quit
Please enter a number from the menu: 0

You selected List

Adam Axelson adam@name.com 654321
Ben Afflec ben@hollywood.com 123456

====Address book====

0 List
1 Add
2 quit
Please enter a number from the menu: 2

Bye!

Verify that the application indicates that the correct number of contacts was loaded on startup, and that the "List" menu option lists the contacts you added in the last run of the application.

If you need to start over, you can remove the address book file from your home directory. On GNU/Linux the file should be in /home/USERNAME/.address_book on Mac OS it should be in /Users/USERNAME/.address_book and on Windows C:\Users\USERNAME\.address_book

If there are any errors/exceptions, they should be logged to .address_book.log also in your home directory. Note that USERNAME should be replaced with your actual USERNAME on your computer!

When you have verified that the application and menu is working, you are done with the assignment!

Code provided by the teachers

Luckily for you, you don’t have to write everything from scratch. We have provided some functionality to get you started. Below follows descriptions of the parts we provide for you.

Text-based (command line interface) Menu API

The menu API consists of a class for a menu, called Menu, defined in Menu.java and an interface for the actions to be performed when the user selects one menu item.

The MenuAction interface (defined in MenuAction.java) needs to be implemented for each menu item you add to the menu. Here’s a sample implementation showing how to use the menu API:

package org.addressbook.ui.cli.menu;
import java.util.Date;

public class MenuExample{
  public static void main(String[] args){
    Menu m = new Menu("this is a menu");
    m.addMenuItem("Print today's date", new MenuAction(){
        public void onItemSelected(){
          System.out.println(new Date());
        }
      });
    m.addMenuItem("Print system info", new MenuAction(){
        public void onItemSelected(){
          System.out.println(System.getProperties().get("os.name")
                             + " - Java: " 
                             + System.getProperties()
                                     .get("java.version"));
        }
      });
    m.addMenuItem("Say hello", new MenuAction(){
        public void onItemSelected(){
          System.out.println("Hello");
        }
      });
    m.start();
  }
}

Here’s a sample run of the MenuExample program (some text is entered by the user):

====this is a menu====

0 Print today's date
1 Print system info
2 Say hello
3 quit
Please enter a number from the menu: 0

You selected Print today's date

Fri Aug 26 15:19:36 CEST 2016

====this is a menu====

0 Print today's date
1 Print system info
2 Say hello
3 quit
Please enter a number from the menu: 1

You selected Print system info

Linux - Java: 1.7.0_101

====this is a menu====

0 Print today's date
1 Print system info
2 Say hello
3 quit
Please enter a number from the menu: 2

You selected Say hello

Hello

====this is a menu====

0 Print today's date
1 Print system info
2 Say hello
3 quit
Please enter a number from the menu: 3

Bye!

Use this example to see how you can implement the menu for the program!

TextUtils class with askFor() method

There is a TextUtils class which declares one static method askFor(String prompt) which will print the prompt to the standard out and read the reply from standard in. In this version, it will not handle null results (the user enters end-of-file), nor will it trim() the answer. This is left for the assignment 3.

Here’s an example program provided by the teachers showing how to use the askFor() method:

package org.addressbook.textutils;

public class TextUtilsExample{
  public static void main(String[] args){

    String favoriteColor = TextUtils.askFor("Your favorite color");
    String favoriteFood  = TextUtils.askFor("Favorie food");
    System.out.println("Your favorite color was: " + favoriteColor);
    System.out.println("Your favorite food was:  " + favoriteFood);
  }
}

Here’s an example run of the program:

$ java -cp bin org.addressbook.textutils.TextUtilsExample
Your favorite color: Yellow
Favorie food: Kebab
Your favorite color was: Yellow
Your favorite food was:  Kebab

Interface describing a mutable collection

The contacts of the address book will be saved in a collection with a few public methods described in the interface SimpleMutableList:

org.addressbook.storage
Interface SimpleMutableList<E>

void	addEntry(E entry) - Let's you add a new entry to the list.

void	listEntries() - Lists the entries to Standard out.

void	load() - Loads the list.

int	numberOfEntries() - Returns the number of entries currently in 
                         the list.

void	save() - Saves this list.

boolean	contains(E entry) - Returns true if the list contains the 
                               entry and false otherwise.

SimpleAddressBook

This is an implementation of the SimpleMutableList interface. This is the class you will use to represent the address book in this assignment. The class compiles but one of the methods are not implemented fully. The incomplete method is: add(Contact c)

You may use this file and only complete the add() method, or, as you can see in the Task 3 description above, write your own complete implementation from scratch.

Contact class

Also, we have provided you with a partial implementation of the Contact class, which you will use to represent one entry in the SimpleAddressBook. A Contact instance will represent one entry in the address book and will hold information about the contact’s name (a String with first and last name), email (a String) and phone (also a String).

Your task is to complete the Contact class, by writing implementations of:

  1. The constructor - it should save the parameters in instance variables
  2. The compareTo() method from implementing the Comparable interface - it should only care about comparing the name of the contacts.
    1. See the comments in the source code for hints.
  3. The equals() method inherited from java.lang.Object - it should only care about the name of a contact, so that two contacts with the same name will be treated as equal regardless of the rest of the information (such as email and phone).

Why does the class Contact override equals() and hashCode()? Because we want to be able to search for a contact in a list of contacts in later assignments, and also use the contains() method of the address book. The only way to find out if a Contact is present in some list, is to check whether the object equals() one object in the list. Sometimes hashCode() is used for this, but we have implemented hashCode() for you (it’s really simple in this case since we only need to use the name of a contact, and since the name is a String reference, we use the hashCode() in java.lang.String).

Why does the class Contact have a compareTo() method? It is because the class implements the Comparable interface! And, why does it implement the Comparable interface? It is so that we can sort a list of contacts in for instance the SimpleAddressBook class, using Collections.sort(). In order for Collections.sort() to be able to sort the entries in a collection, it needs to compare the entries to each other (to see which entries are “smaller”, “equal” or “larger” in order to do the actual sorting). For this reason, Collections.sort() only works on collections whose entries are “Comparable”. Entries whose class implements Comparable can be compared to each other using the compareTo() method which must be implemented.

Why does the class Contact also implement the Serializable interface? Classes that implement Serializable doesn’t have to implement any methods, actually. The interface declares no methods (it’s a so called marker interface). But objects created from such a class can be saved on for instance a filesystem, and later be read back to a Java program. This allows our program to save the AddressBook object (which has a List of Contacts) to be saved to the filesystem so that we can make the contacts persistent between runs of the program. You do not have to do anything, since we have already written the the Contact class stub for you. It might be interesting to know, though, that all you need to do is to mark a class as “implements Serializable” in order to be able to save objects of the class to a file (or send them over the network etc).

HTML documentation (generated from javadoc comments)

In the doc directory, you’ll find the API classes documented. This might help you get an overview of the package structure, and the different interfaces and classes for each package.

Open doc/index.html in a browser to browse the API documentation.

Requirements for passing the exam

Functional requirements

The two required functions of the SimpleAddressBook class accessible via the text based menu are 1. Listing the current address book contacts and 2. Adding a new Contact to the address book.

The listing functionality is already implemented in the source code provided, so the student must implement the add() method in the source code for SimpleAddressBook.

The SimpleAddressBook class has a load() and a save() method already implemented, using a file in the user’s home directory.

The application must have a text-based menu system presenting two options for the list and add functions. The student should call load() on the SimpleAddressBook in the constructor for SimpleApplication, so that (if the file exists) the address book file is loaded on application startup. Likewise, the save() method should be called on the SimpleAddressBook in the MenuAction for adding a contact, so that added contacts are immediately saved in the file.

This leads to a functional requirement of persistence between runs of the application. Any new Contacts added in one run, should be present on application start up of a subsequent run.

Needless to say, all submitted code should compile.

Finally, the code should pass the tests provided with the source code.

For a pass grade, all functional requirements must be satisfied.

Code and style requirements

All code written by the student should be indented in a style consistent with the provided code.

The directory structure with a division between source code in the src branch and class files in the bin branch should be followed.

The naming of the classes which the students write from scratch must follow the instructions, so that the teachers can compile and run the application:

$ java -cp bin/ org.addressbook.main.SimpleMain

The above should launch the application and display the menu.

The student must submit a zip file (or a tar.gz file) of one directory containing your name as part of the directory name. In this directory, you should have the bin/ and src/ directories etc.

Trouble shooting

Compiling

You must cd down to the directory where src/ and bin/ are located in order to compile. In order to compile you need to do two things:

  1. give javac the flag -cp src
  2. give javac the flag -d bin

The first flag, is to give the compiler the class path needed to find the package structure. Inside src the top level package org is located. In order to compile the classes while you are in the directory above src, you must tell the compiler that it should start looking for packages (directories) inside the src directory. The src directory is not part of the package structure, but it is convenient to have all the Java source code files under this directory.

The second flag, is to give the compiler a directive for where to create the resulting class files. Telling it to put all the class files under the bin directory lets you separate the source code directory tree from the binary class file directory tree.

The full command for compiling, e.g. the source code for org.addressbook.storage.Contact from the directory above src now becomes:

$ javac -cp src -d bin src/org/addressbook/storage/Contact.java

Note that the argument to javac is the relative path to the source code file (of course). But if the source code file imports some classes from a package located under the src directory structure, the compiler would not be able to resolve this without the classpath src. Giving the classpath src will let javac find all packages and classes referred to by the source code file given as argument.

Running files

Since all the class files should be created under the bin directory, we need to give java the class path bin in order to find the class files in packages.

If we, for instance want to run the SimpleMain class in the package org.addressbook.main from the directory above bin, we need to to this:

$ java -cp bin org.addressbook.main.SimpleMain

This allows us to run the main class from the directory above bin. Using classpath (the -cp flag) allows us to do all work from the directory containing both src and bin. There is therefore no need to ever leave the directory where you find both bin and src.

Running the test files

The test programs we have provide you with are using a mechanism called assertions. An assertions checks a condition and if the condition is not met (evaluated to false) the test should fail with an error message. But in order for Java to care about assertions, you need to provide the flag -ea (enable assertions). To run the test program for the Contact class, you should use this command line (note that running the test from the directory above bin requires both the classpath flag and the -ea flag!):

$ java -cp bin -ea org.addressbook.tests.TestContact 
=====Testing constructor=====
Testing new Contact("Name Nameson", "email@email.com", "123")
* Constructor test for contact: Name Nameson email@email.com 123 passed.
=====Testing constructor=====
Testing new Contact("Bob Bobson", "bob@email.com", "1234")
* Constructor test for contact: Bob Bobson bob@email.com 1234 passed.
=====Testing constructor=====
Testing new Contact("Charlie Ceeson", "charlie@email.com", "12345")
* Constructor test for contact: Charlie Ceeson charlie@email.com 12345 passed.
=====Testing constructor with null checks=====
* Constructor null tests passed.
=====Testing compareTo=====
* Test of compareTo() in Contact passed!
=====Testing equals=====
* equals() test for Contact passed.

Knowing how to get started on the assignment

Read the section above called Getting started. There you’ll find four main tasks, which should be completed in the same order as listed. The first task is to finsh the Contact class. The second task is to finish the SimpleAddressBook class. The third task is to finish (or optionally create) the SimpleApplication class. The fourth and last task is to write the SimpleMain class with the main method for the whole application.

Do the tasks in order, and use the test programs to verify that you have completed the tasks. Test programs are available for task 1 and task 2. In order to verify task 3 and four you need to finish both and run the SimpleMain program.

Discussion on the design of the system

The design of this system is far from perfect. There are a number of design decisions which are questionable and subject to a further elaboration. We will list some design decisions here and say a few words about each, in order to get you thinking about different ways in which this simple system could have been designed. We’ll bring up possible different approaches which could have been taken and a few words on what consequences such differences could have for the maintainers and developers of this system.

Division into packages and naming of packages

These are the packages in this system:

  • org.addressbook.main
  • org.addressbook.storage
  • org.addressbook.tests
  • org.addressbook.textutils
  • org.addressbook.ui.cli.menu

The top level package name is org.addressbook which might be unorthodox. Usually the top level has the form of a domain name, with the parts written in reverse, like se.chalmers . It is hard to imagine a domain name of “addressbook.org” but it isn’t impossible. This could have been chosen to be a more realistic (and probably longer) name.

The next thing we notice about the package names, is the org.addressbook.storage package. The name tells us that the classes in that package should deal with storage (probably storing the addressbook on the filesystem or in a database etc). But in the package we find the following classes:

  • Contact
  • SimpleAddressBook
  • SimpleMutableList

At least the Contact class seems to have little to do with storage. This package could have been divided into more packages (with better names).

The test programs for testing various classes while developing them belong to the org.addressbook.tests package. It is perhaps questionable if the tests should be part of the application API (part of the class structure for the application). If this system were to be packaged and shipped to a customer, the tests package could be removed.

The package org.addressbook.ui.cli.menu is perhaps a little to long and specific. There is only one meny system and that one is text-based (command line interface, cli). It is questionable whether adding a graphical interface to this system is something that is simple to do, and if one did, if there would be a menu subpackage under e.g. org.addressbook.ui.gui... Perhaps it would have been fine to settle with a shorter package name such as “org.addressbook.menu” or possibly “org.addressbook.textmenu”.

The capabilities of the SimpleMutableList

The interface for grouping elements together in a list, is the SimpleMutableList interface (implemented by SimpleAddressBook). It publishes the following methods:

  public int numberOfEntries();
  public void listEntries();
  public void addEntry(E entry);
  public void save();
  public void load();
  public boolean contains(E entry);

One thing which could be discussed about this design decision it the listEntries() method, which is declared void (it doesn’t return anything, it just does something) and takes no arguments.

Listing the entries of the list in an implementation such as the SimpleAddressBook, requires the implementation to choose how to list the elements. The way the SimpleAddressBook implementation does it, is to iterate through the elements and print them to standard out. This choice might be a little awkward, since the client code of the SimpleAddressBook can’t override this decision - client code is stuck with the decision of interpreting listEntries() as “listing the entries to standard out”. One way of making the implementation SimpleAddressBook a little more flexible could be to accept a reference to e.g. a PrintStream in one constructor, and having listEntries using that PrintStream for listing the entries.

One could also discuss whether a “SimpleMutableList” should be able to list itself at all. That might be a matter of style and taste. If the list can “list itself” this makes the burden less for client code (which can simply tell the list to list itself and trust that it will be done in a good way). If a different approach had been chosen, like letting a SimpleMutableList have a method which would return all entries as a java.util.List, then the responsibility (and burden) of listing the entries (to standard out or elsewhere) would fall on the client code! The design decision we made, to let SimpleAddressBook just be able to list itself using standard out, is something we hope will trigger your critical thinking.

Other design decisions

The address book file for persistence, as well as the log file for errors, are files which are private concerns of the SimpleAddressBook class. Perhaps it would have been wiser to factor these facts out to some other class, and provide methods for accessing (and possibly setting them so that the files are customizable). As it is now, the main method in SimpleMain can only detect that an error has occurred, but can’t direct the user to the log file, because the location of the logfile is hidden in a private variable of the SimpleAddressBook class!

The org.addressbook.textutils.TextUtils class uses System.console().readLine() in its askFor() method. This doesn't work well with IDEs like Eclipse (and actually not well with Cygwin on Windows either), so the method has been fixed to use a Scanner on System.in if there is no system console. It could be argued that only using a Scanner would have been enough, since it would have worked well with all platforms. We left the conditional use of either the console or the Scanner in the method, so that you may look at a way to make sure there is a System console before using that approach. The autors, personally, prefer the System.console().readLine() approach since it is cleaner than using a Scanner in our opinion. Our experience tells us that Scanners usually are confusing to students (particularly in the way it handles newlines) but it doesn't hurt that you see more than one approach for reading input from the user.

The method now reads:

  public static String askFor(String prompt){
    String result=null;
    System.out.print(prompt + ": ");
    if(System.console() == null){
      result = new java.util.Scanner(System.in).nextLine();
    }else{
      result = System.console().readLine();
    }
    return result;
  }

It might be a good idea to always check that there is a system console, by checking that System.console() doesn't return null, before using the system console ;-) We first didn't check ourselves, since we were convinced that Cygwin would offer a system console to Java (but we were wrong). So now we follow our own advice and check for null before using the reference supposedly returned by System.console(). Now you know the story behind leaving the method as above, using either the System.console().readLine() call, or a Scanner connected to System.in.

Can you find other questionable decisions? Please include them in a report in your zip file!

Assignment #3 - refactoring the AddressBook

Description

This assignment is about refactoring (changing) the design of the application and writing documentation. We'll address some of the problems with the design decisions made by the teachers in the assignment 2 version of the application layout.

We'll fix the packages so that for instance storage contains only classes dealing with storing the contacts. We might even change the name of storage to something more descriptive! This will help new developers better understand where to find the classes of the system, if the project grows and we get more members in our team working with the application.

Documentation is also important for many reasons. One reason is to guide newcomers to the project so they better understand how the system works. Another reason is to document for ourselves so that we know what we did when we re-visit the code if someone files a bug report or requests a new feature. Another reason could be that the customers who buys this fancy application might get the source code along with the application so that they too can develop the application further. In order for the source code to be useful for the customer in such a case, it needs to be documented.

We'll focus on writing Javadoc style comments for some classes, and generating documentation as HTML in this assignment.

Task 1 - Fixing the directory layout and package names

Currently, this is what the source code layout looks like:

src/
└── org
    └── addressbook
        ├── main
        │   ├── SimpleApplication.java
        │   └── SimpleMain.java
        ├── storage
        │   ├── Contact.java
        │   ├── SimpleAddressBook.java
        │   └── SimpleMutableList.java
        ├── tests
        │   ├── TestContact.java
        │   └── TestSimpleAddressBook.java
        ├── textutils
        │   └── TextUtils.java
        └── ui
            └── cli
                └── menu
                    ├── MenuAction.java
                    ├── MenuExample.java
                    └── Menu.java

Let's create a more logical source code tree with better names for the packages!

We'll start with a better name for our organization. Let's call it contactcompany.

So we'll start from scratch in a completely new directory on our computer, say a directory somewhere called assignment3. This directory is a directory where we do our work and it is not part of the product we are writing. It is just our working directory for this assignment.

In this directory, we'll start with the directories for the packages of our product:

$ cd the_name_of_our_working_directory # it could be for instance ~/java-course/assignment3
$ mkdir -p src/org/contactcompany #this (org) is the root package for our application

The src directory is not part of the application package structure. It is only a directory on your computer which contains the source code files and the actual packages for your application!

What are the main parts of our application? We could decide that they are:

  • The class with the main-method
  • The class which creates the menu and user interface
  • The API for creating a menu with menu-actions
  • The helper class which gives us the askFor() method which asks the user for something and returns it as a String
  • The Contact class which we use to create objects representing our contacts in the address book
  • The SimpleMutableList<E> interface which we use to create a list of, for instance Contact references
  • The SimpleAddressBook which implements SimpleMutableList<Contact>
  • Some example code (we can delete those now)
  • Some tests

If you come up with a different analysis, you may stick to that instead but then our guidance won't work for you. We recommend that you pretend that you agree with our list (and if you have opinions about it, document them and propose in text a different design).

Based on the list of components and classes, we suggest the following new package structure (you should make these directories under src):

.
`-- org
    `-- contactcompany
        |-- api
        |   |-- container
        |   |-- entry
        |   |-- ui
        |   |   `-- textmenu
        |   `-- util
        `-- application
            |-- list
            |-- main
            `-- ui

This is an arbitrary recommendation and just a suggestion. It's OK if you pretend that some one (like a project manager or architect or so) just decided that this is the new layout.

Task 2 - Thinking about the new package layout

The thought behind this new layout is that all our packages belong to the top package org.contactcompany. Also, we divide our project into two subpackages: org.contactcompany.api for stuff we might reuse in other projects, and org.contactcompany.application for stuff we write for this particular application of our contact book.

The org.contactcompany.api has the following subpackages:

  • org.contactcompany.api.container - for the SimpleMutableList interface (to start with)
  • org.contactcompany.api.entry - for the Contact class (to start with, perhaps we'll add other classes which can represent entries in the list later)
  • org.contactcompany.api.ui.textmenu - for the text-based menu system classes providing means to create menus with menu actions
  • org.contactcompany.api.util - for the TextUtils class (to start with, perhaps we'll add more utility classes later)

The org.contactcompany.application package has the following subpackages:

  • org.contactcompany.application.list - this application's implementation of SimpleImmutableList
  • org.contactcompany.application.main - this application's main-class with the main method
  • org.contactcompany.application.ui - this application's user interface, which is a text-based menu, made from the stuff in org.contactcompany.api.ui.textmenu

Task 3 - Move the old classes into the new package structure

If you are happy with this layout and division of packages (you can pretend that you are - these decisions are always subject to discussions, opinions, matter of style etc), then it is time to move the sourcode files into this new package structure. Of course you will have to update all the package-declarations and import statements accordingly.

This is what you want to achieve:

.
`-- org
    `-- contactcompany
        |-- api
        |   |-- container
        |   |   `-- SimpleMutableList.java
        |   |-- entry
        |   |   `-- Contact.java
        |   |-- ui
        |   |   `-- textmenu
        |   |       |-- MenuAction.java
        |   |       `-- Menu.java
        |   `-- util
        |       `-- TextUtils.java
        `-- application
            |-- list
            |   `-- SimpleAddressBook.java
            |-- main
            |   `-- SimpleMain.java
            `-- ui
                `-- SimpleApplication.java

Study this new layout and make sure you understand (not necessarily agrees with) the new package layout. The main new idea we want to reflect in the new directory layout is that we separate classes we could reuse in another application from classes written only for our address book application. This means that under org.contactcompany.application we put only the stuff we need for our address book application:

  • a class with main
  • a text-based user interface
  • a list of all contacts

In order to create the text-based user interface, we are using our own api for creating a text menu. This means the Menu class and implementing the MenuAction interface for each menu item.

In order to create the list of Contacts, we use our own api for creating an implementation of the interface SimpleMutableList<Contact>.

Now, main can create the text-based ui, and the text-based ui can create a Contact list which it will manipulate from the menu item for "add" and "list" for instance.

Task 4 - Change the names of some classes

There's a few more things we'd like you to do. The application's ui (user interface) is represented by a class called org.contactcompany.application.ui.SimpleApplication. Perhaps this name is not self-explanatory. Rename the class to org.contactcompany.application.ui.MenuWithContactList (we're not convinced that this is the best name, but at least it tells new programmers more than the old name SimpleApplication - what is a simple application?).

Also, rename the class org.contactcompany.application.list.SimpleAddressBook to org.contactcompany.application.list.ContactList, because this explains better what the class is representing - a list of contacts.

Finally, rename the class org.contactcompany.application.main.SimpleMain to org.contactcompany.application.main.AddressBook. We can tell by the package name that the class contains the main method (the part of the package name saying .main. tells us that!) and for the user running the program it might be a more intuitive name for a program with an address book, to say AddressBook when we talk about the actuall program starting point!

Now the directory should look like this:

.
`-- org
    `-- contactcompany
        |-- api
        |   |-- container
        |   |   `-- SimpleMutableList.java
        |   |-- entry
        |   |   `-- Contact.java
        |   |-- ui
        |   |   `-- textmenu
        |   |       |-- MenuAction.java
        |   |       `-- Menu.java
        |   `-- util
        |       `-- TextUtils.java
        `-- application
            |-- list
            |   `-- ContactList.java
            |-- main
            |   `-- AddressBook.java
            `-- ui
                `-- MenuWithContactList.java

Task 5 - Change package and import declarations

Make sure each source code file has the right class name, the right package declaration and the right import statements.

The class org.contactcompany.application.list.ContactList now must import the SimpleMutableList and Contact classes, because it uses those names already in the class declaration:

import org.contactcompany.api.container.SimpleMutableList;
import org.contactcompany.api.entry.Contact;
// more imports
public class ContactList implements SimpleMutableList<Contact>{
// the rest
}

Do this exercise with every class and make sure thay can import the right stuff, and that they declare that they belong to the correct packages.

Task 6 - There's more to change!

You must also change the constructors of the classes you gave new names. The constructor for MenuWithContactList can't be named SimpleApplication any more, since we have changed the class. So we need to change all the constructors for the classes with new names.

Task 7 - Change what your classes are using

Think about what objects your classes are using. The AddressBook with the main-method should no longer create a new SimpleApplication, because there is no such class any more! It should create a new MenuWithContactList instead.

The MenuWithContactList uses Menu, MenuAction, SimpleMutableList<Contact>, Contact and ContactList. These new names should be used in the code, and the classes and interfaces need to be imported.

Task 8 - Make sure you have an src directory and a bin directory

Create an src directory and move the org directory into it (if you haven't done so already). You should continue to work in a directory which is not part of the application itself, and in this directory you should have two more directories which are also not part of the application's package names etc:

$ mkdir src
$ mv org src/

Create a bin directory for your class files:

$ mkdir bin

If you now do ls you should only see two directories, bin and src.

The directory you are working in should now have the following structure:

.
|--bin
`--src
   `--org
      `--etc etc    

(where etc etc are the directories under org)

Task 9 - Compile the class with the main

Compile the AddressBook class (with the main method) using src for class path and bin as destination:

$ javac -cp src -d bin src/org/contactcompany/application/main/AddressBook.java

Verify that all classes were compiled and ended up under the bin directory.

If you run into problems, you have probably forgotten to change one or several of:

  • Class declarations - the class declaration should reflect the file name
  • Constructors - the constructor of a class uses the name of the class as part of the definition
  • Package declarations - the package declaration should reflect the relative path from the src directory to the source code file
  • Import statements - If a class uses a class or interface from another package, you must import the package and name
  • Types of objects a class is using - if your class before was using a SimpleAddressBook for instance, it should now be using a ContactList

Task 10 - Verify that you can run the application

Running the main class should be simple:

$ java -cp bin org.contactcompany.application.main.AddressBook

Of course, you should never leave the directory where you have the src and bin directories. Staying in that directory lets you stay in one place and instead use the -cp flag for compiling and running. You don't have to use any .. as part of any paths, which could be a good thing.

In order for the compiler to verify the import statements (all your packages start with org.) from the current directory above src you pass along the flag -cp src to javac so it knows where to look for every package starting with org.. And in order for the compiler to create the class files under bin, you pass javac the flag -d bin.

In order for the JVM to find your packages starting with org., you send along the flag -cp bin to the java command.

Requirements for a passing grade

In order to get a Pass on this assingment, you should complete the 10 tasks above. Here are the requirements:

  • You should zip your application with the bin and doc(if you have one) directories empty, and only source code in the src directory.
  • Your application must compile
  • Your application must run correctly
  • You must have changed the class names and package names (we recommend our suggestions but if you have better names, feel free as long as the code compiles)
  • Your code should use a consistent indentation

About the optional tasks

If you opt to do also the optional tasks, make sure your application works and compiles according the the requirements above.

You don't have to send in the generated javadoc HTML files etc. The doc directory should be empty.

Optional task 1 - Let's fix some stuff missing in the assignment 2

Optional task 1.1 Fix the askUser() to handle null and trim the result

It would be nice if org.contactcompany.api.util.TextUtils.askfor() didn't return null if the user types Ctrl-d for instance. This is easily fixed!

Before returning the String the user entered in the askFor() method, check if the String is null. If it is, re-assign the String to the empty String instead ("" is the empty String).

It would also be nice if org.contactcompany.api.util.TextUtils.askfor() removed leading and trailing whitespace from the String before returning it.

That is also easily fixed! String has an instance method called trim() (with no parameters) which returns a reference to a new String without any leading or trailing tabs or spaces. Call trim() on the reference in the return statement.

What's the point of letting askUser() do this job? Now, the menu item for adding a contact always gets a good String (which is not null and always is trimmed). This prevents us from the programming error (bug) of calling the constructor for Contact with a null reference for name, which would make the program crash!

We still want Contact to behave the way it does, because we might use it for some other project in the future. Having Contact throw a NullPointerException when name is null, forces us to make sure we don't ever send null as a name, also in that project!

It was a good thing that Contact's constructor threw a NullPointerException, because our address book application would crash with an error message if name was null or the empty String (an empty string for name actually would make the constructor throw an IllegalArgumentException, but the principle holds). If we don't want that to happen, we had better fix the source of the null which was the askUser() method. And now we've fixed that!

Optional task 1.2 - Fix the menu item for "add"

Now that we know that askUser() can't return null, we know that it still can return (a reference to) an empty String. But the constructor for Contact doesn't like a name with an empty String as the value. See assignment 2 if you don't remember!

If the user enters nothing for name in the menu item for "add contact", the menu item will create a new Contact with an empty String as the argument for the name parameter. The constructor will get upset with us and not accept this and throw an IllegalArgument in our face, and the program will crash.

So rather than letting the user set name for the new contact to nothing and having our program crash, let's fix the MenuAction for the "Add contact" menu item.

In the code for this MenuAction we get name, email and phone from the user, using askUser(). After this, let's add a while loop which does the following:

While the name variable equals "", print a message saying name cannot be empty and call askUser() again and assign the result to name.

This loop will detect an empty String value for name, and complain to the user and askUser() again until the user of the program enters something which is not an empty String.

Pseudo-code (this is not pure Java syntax, simply an explanation of what to do):

while "" equals name
   print error message about the fact that name cannot be empty
   name is assigned a new call to askUser("name:")
end-while

Re-compile and verify that it works.

Optional task 1.3 - Preventing construction of TextUtils instances

The TextUtils class is meant as a utility class with currently one public static method, askFor. It is not meant as a class for creating objects, only as a convenience class. So let's prevent people from creating instances of the class.

Create a no-arguments constructor in the TextUtils class with nothing in the block of the constructor. Make the constructor private, save and re-compile.

It is common practice to actively prevent people from creating instances of classes which exist only to offer some static helper methods. This technique is used in the Java API for e.g. the java.lang Math and System classes.

Optional task 1.4 - Documenting the new behavior of askUser()

Open the source code for TextUtils again and put a comment above the askUser() method explaining how it will work.

The new behavior could be documented like this:

  /**
   * Asks the user a question and returns the result
   * <p>
   * The String returned cannot be null (if the user creates a null,
   * the empty String will be returned). The String returned will be trimmed
   * so no white space remains before or after the text in the String.
   * </p>
   * @param prompt The question to ask the user, e.g. "Name: "
   * @return The user's reply as a reference to a String
   */

Optional task 2 - Generating documentation for the project

Start by creating a doc directory in the same directory as you have src and bin.

Next, it is time to generate the documentation from all the Javadoc style comments (it is a long command, so you might have to scroll to see it all):

javadoc -sourcepath src -d doc -link  http://docs.oracle.com/javase/7/docs/api/ org.contactcompany.api.container org.contactcompany.api.util org.contactcompany.api.entry org.contactcompany.api.ui.textmenu

The command ends with a list of all the packages to include. The HTML documentation will end up in the doc directory you just created for this purpose. To see the documentation, open the file doc/index.html in a browser. Navigate to the TextUtils class, and see if your documentation of the askUser(String) method worked.

You can add as many packages you want, if you want to include more packages in the documentaion. The command line above focused on the org.contactcompany.api family of packages.

This is what the web page with the generated documentation could look like:

Generated documentation

Reading exercise for Optinal task 2 - Different types of documentation

Note that we chose to document only the API part of our project, that is, the packages we think we might reuse in other projects. The org.contactcompany.application packages are not meant to build upon in other projects and are more of the type that they contain implementations using (and/or implementing) our own API. These classes and packages under org.contactcompany.api probably merits a little different kind of documentation, more targeted on developers of this particular application. Of course we should document also this part but we wanted to take the opportunity do introduce you to one way of thinking about Java code. When we write Java code we sometimes write an API to be used by other programmers (which could include ourselves in new projects). Other times we are using our API and those of Java and potentially third party APIs, to write code for a new application.

There is a difference here. When we write an API of reusable classes and interfaces, our target audience for the comments are anyone (including ourselves) who would use our API in some application, in order to gain something. When we write a particular program or application, the documentation has a slightly different target audience. We document what we are doing in terms of our application, which allows us to use a language with terms and abstractions relative to the application at hand. The readers of the documentation of our application code are people working on (or perhaps also with) the application.

The readers of the API documentation could be people developing new applications we have no idea about when we write the API. Note that this description of the two types of code is very simplified in order to make a point. The authors experience with teaching Java tells them that a source of confusion for new Java students are that it is not always clear what the purpose of a class or interface is. Sometimes we write code merely as part of some program. A program can be as small as one single class with a main method (odd but feasible). Sometimes a small application contains only of the main class and some classes for some objects which makes the main method's job easier. Other times, we teach the students how to write more general classes without a clear application in mind. Such general classes could be used in a variety of real applications. The problem for the students is that it is not always clear what purpose some example class is serving - is it a general class for a number of potential future applications, or is it a very specific class we need in some concrete application?

We hope that the new layout of the packages, classes and interfaces for the address book application helps you thinking about these different roles of Java code. The API part of this assignment packages could be thought of a more abstract and general type of code which we can use not only in an address book, but perhaps also in other applications (even if they would have some similar domain, like a list of objects and a textual user interface menu system). The application part is meant to start you thinking about coding for a specific application, sometimes using the API to make the coding easier.

You can also think about the official Java API and its documentation. The Java API was written with no particular application in mind and meant to be very general so that it fits the needs of as many applications as possible. The target audience of the official Java API documentation are Java programmers in general but also implementers of the Java platform. If someone wants to create a JVM for a new hardware or operating environment (or both), they would need to also provide the Java API classes and packages functional for this new platform. For this reason, the Java API documentation is also a specification of the Java Development Kit and Java Runtime. You can read more about this here (Oracle.com) and here (also Oracle.com.

For a general tutorial on how to write Javadoc style comments, we recommend reading this rather dry page first and then perhaps looking at this tutorial from North Carolina State University.

Links

External links

Books this chapter is a part of

Programming with Java book

previous chapter Book overview