Java:Assignment - Address book - About the code

From Juneday education
Jump to: navigation, search

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

Address book - 04 - 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();
  }
}

To run the MenuExample program, type:

java -cp bin org.addressbook.ui.cli.menu.MenuExample

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

Address book - 05 - TextUtils

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.

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! You are allowed to change the access modifier of the log file name to public.

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!

A note on the location of the addressbook and log files on Windows

If you are running Windows and Cygwin, note that the log file won't end up in the Cygwin directory /home/your-user-name- It will end up in C:\Users\your-user-name\ . This is because java is a Windows command, and not a Cygwin command. So even if you run java from Cygwin, it will still be a Windows command, and therefore it will resolve the home directory to the Windows home directory. Ask your teachers and supervisors if you need more explanations on this matter.

If you run GNU/Linux or Mac OS X, your files will end up in the "normal" home directory.

Forcing a file error while the application is running

The above information is useful if you want to test that your exception handler works when the address book file doesn't exist or is corrupted. For instance, you can try what happens if you start your address book application, adds a few names, and remove the address book file while the application is still running in a different terminal. What happens when you try to list or add names again?

Links

Where to go next

The next page describes how the address book assignment will be examined: Java:Assignment_-_Address_book_-_Examination

« PreviousBook TOCNext »