Tutorial 2: JavaDoc, Package, Factory


Introduction

This tutorial covers:


I. Java Documentation Style

The Java Development Kit (JDK) comes with a tool called JavaDoc.  This tool will generate documentation for Java source code with comments written in accordance with the Java documentation style.   Take a quick look at the following links to see more examples.

Create a directory ~/comp212/tutorials/02 for this lab and copy the file ~comp212/tutorials/02/*.java.   These are the Java source code for the (Scheme) lsit implementation.  They are documented according the JavaDoc convention.  Use your favorite editor to examine AList.java.

Exercises:

  1. To run the javadoc utility, execute the command: javadoc *.java.   Appropriate html files will be generated.  What are the generated html files ?  Use a browser to view them.  Are the private fields and methods displayed?
    Now execute the command: javadoc -private *.java.  Can you see the difference?
  2. Execute the command: javadoc.  You should see a brief description of the usage of javadoc.  Take a brief look at the explanation of the flags.

II. Package

A Java package is a grouping of classes similar to the notion of a directory is a grouping of files.  Packages are used to help avoid name clash and to hide particular pieces of code from the clients.  A package has a name, such as utility, or java.lang.util, etc.   In general, a package name has the form A followed by zero or more strings of the form .B where A and B are strings beginning with an alphabet character.  To make a java class part of a particular package, say scheme, all you have to do is to add the statement package scheme; to the very top of the class source file.   Also, you will need to put the file in the directory structure that mirrors the package name.  For example, the java classes that belong to package scheme should be in a directory named scheme.

Exercises:

  1. Add the package scheme; statement to the top of AList.java.  If we try javac AList.java now, we will get an error message.  Try it to see what happens. You will need to create a subdirectory called scheme and move AList.java into the scheme subdirectory.  Now stay at the directory above scheme and compile using javac scheme/AList.java.  You should succeed this time.

  2. Add the package scheme; statement to the top of EmptyList.java and NEList.java.  
    Move these two files to scheme
    Remove the public access from the class EmptyList and the class NEList.
    Do not make ListClient.java part of the scheme package.  ListClient.java does not have a package name.  It is said to be in the no-name (or default) package.   Leave ListClient.java in the ~/comp212/tutorials/02 directory.  If you try to compile ListClient.java now, you will get an error message.  Try it to see what happens.  You will need to add the statement import scheme.*; to the top of the file to indicate to the compiler that you are using all the public classes in the scheme packages.
    Try to recompile ListClient.java after adding the appropriate import statement.  You should see a few error messages saying that you can't use EmptyList.java and NEList.java because these classes are not public.   This is because the ListClient class is not part of the scheme package.
    You can resolve this problem by making ListClient part of the scheme package.  A class of a package can access all the classes (public or "package-private") in the package.  However this is not a good solution in general because a client may be using many classes from different packages and cannot be part of more than one package.  The next section will show you a better solution using a "factory".

    II. Factory

    Good software engineering practices advocate the principle of "information hiding": all implementation details should be hidden from client code.  In the case of our Scheme list system,  EmptyList.java and NEList.java are the details that clients should not know about.  The client should only program to the AList abstraction.  For this reason EmptyList.java and NEList.java are not public classes.  But the client needs to have a way to create concrete instances of  EmptyList  and NEList somehow.  The solution is to add a public class that is part of the scheme package and that knows how to call the constructors for EmptyList.java and NEList to manufacture these classes.  Such a class is called a factory in the language of design patterns.  The following is an example of such a class.


    package scheme;

    /**
    * Manufactures AList objects.
    * Implemented as a singleton.
    *
    * @author Dung X. Nguyen
    * @version 1.0
    * @since 09/10/00
    * @Custom Copyright 2000 -All rights reserved
    */
    public class ListFactory
    {
        public final static ListFactory Singleton = new ListFactory ();

        private ListFactory()
        {
        }

        public AList makeEmptyList()
        {
            return EmptyList.Singleton;
        }

        /**
        * Creates a non-empty list with a given non-null first element and a non-nul tail.
        * Throws an IllegalArgumentException if any of the parameters is null.
        *
        * @param dat != null, the fist data element of the non-empty list.
        * @param tail !- null, the tail of this non-empty list.
        */
        public AList makeNEList(Object dat, AList tail)
        {
            if (null == dat || null == tail)
            {
                throw new IllegalArgumentException ("dat and tail must be non-null.");
            }
            return new NEList (dat, tail);
        }


        /**
        * A few simple test cases.
        */
        public static void main (String[] args)
        {
            AList pc0 = ListFactory.Singleton.makeEmptyList ();
            AList pc1 = ListFactory.Singleton.makeNEList (new Integer (-1), pc0);
            AList p1 = ListFactory.Singleton.makeNEList ("-5", pc0); // NOTE: "-5" is a String, not an Integer!
            AList p2 = ListFactory.Singleton.makeNEList (new Integer (2), p1);
            AList p3 = ListFactory.Singleton.makeNEList (new Integer (-4), p2);
            AList p4 = ListFactory.Singleton.makeNEList (new Integer (3), pc1);
            System.out.println ("Empty List = " + pc0);
            System.out.println ("(-1) = " + pc1);
            System.out.println ("(3 -1) = " + p4);
            System.out.println ("(-5) = " + p1);
            System.out.println ("(2 -5) = " + p2);
            System.out.println ("(-4 2 -5) = " + p3);
        }
    }

    Exercise:

    1. Modify ListClient.java to use the factory methods, makeEmptyList() and makeNEList() of ListFactory to create EmptyList and NEList instead.  You should now remove the code that checks for valid input parameters in the constructor for NEList.  Compile ListClient and run it.

dxnguyen@cs.rice.edu
revised 09/10/00