Java:Assignment - Address book - Optional

From Juneday education
Jump to: navigation, search

Optional - refactoring the AddressBook

Description

NOTE:Don't start on this before you have handed in the functional version of the mandatory parts! We will not correct this, and the scripts for correcting the assignment won't work if you start on this before handing in the mandatory part.

This voluntary optional 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 Optional 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.

Optional challenge 2 - Create a GUI

Address book GUI - add contact frame

We have a small book about Swing (a GUI framework for writing graphical Java applications) which includes an example on how to add a GUI to the address book: Introduction to Swing - A GUI for the Address book assignment. As a voluntary, optional challenge, you may create a small GUI for your address book application. You may use our code if you feel like it, or write your own code. Note that this is not mandatory and completely voluntary and serves as a challenge for those who thought the address book assignment was too easy.

Links

Where to go next

This is the last part of the Address book assignment. Click next or TOC to get back to the Programming with Java book.

« PreviousBook TOCNext »