All Categories :
Java
Chapter 29
Documenting Your Code
by George Reese
CONTENTS
If one single thing could define the difference between hackers
and serious programmers, it would be documentation. The trademark
of a hacker is a quick-and-dirty solution developed without using
any commonly understood process or anything other than the code
itself to describe what was done. A programmer, on the other hand,
develops an application based on documentation derived from a
clearly defined development process. As a result, the quality
of the applications built by programmers are worlds above the
quality of applications built by hackers.
This chapter examines why documentation can make such a huge difference
in application quality and how you can effectively document your
work to achieve the highest standard. The first task is understanding
the role of documentation, especially in complex applications.
Sporadic documentation is barely more useful than no documentation
at all. The chapter also discusses approaches to object-oriented
development, which help produce useful documentation, and the
javadoc tool, which provides
API documentation.
The Role of Documentation
The complexity of software engineering
can equal the complexity of fields like construction and manufacturing.
In construction, you create buildings using a process that defines
and documents what you are building before you build it. Specifically,
an architect puts together a blueprint that others use to define
how they will construct the building. Years down the road, if
someone decides to expand the building, or simply update it to
new fire standards, that old blueprint is brought out and used
as the starting point for the changes.
Not all construction projects, however, have the same level of
complexity. For example, building a doghouse is not quite the
same as building a bridge. You do not really need to document
the building of your doghouse; but if you are building a bridge,
you better have it blueprinted and tested in computer simulations
before constructing it. Why should software development be any
different?
Over the life cycle of an application, documentation plays two
important roles:
Prescriptive Documentation
Prescriptive documentation outlines how your application
should be built. How often have you finished a significant
portion of an application that you failed to document only to
realize that you forgot a piece? Because good documentation follows
from a standard engineering process, you are less likely to find
that you have missed a key element; and for each element, you
will understand it thoroughly before you begin building it. The
basic goal of prescriptive documentation is to provide you with
a history of the issues you decided were important and how they
fit into the application as a whole.
Descriptive Documentation
Descriptive documentation details how an application is
actually built. In a well-structured project, your documentation
evolves as the project evolves, continuing even after the application
has been deployed. If you have ever had to modify someone else's
existing code, you know that the tasks of application testing
and maintenance are made much easier by well-written documentation.
And much easier translates into less time and money.
Random documentation does not help you build an application. The
documentation you create should tell you, and any potential reader,
all the information known about the objects in your system and
how they act together. Although scribbling notes into a notebook
may be enough to capture this information for yourself, this approach
does not help others understand what you are trying to do.
Good documentation comes from a sound software engineering process
that challenges you to ask the right questions and record the
answers in a standard format. It is often said that "it's
the planning, not the plan"; to some degree, that cliché
is true. A good software engineering process serves as a sort
of preflight checklist that helps you be sure that you do not
miss anything crucial before takeoff.
This chapter takes a brief look at object-oriented software engineering
and how it relates to the documentation you produce. Many volumes
have been written on the subject of object-oriented software engineering,
so this short chapter does not attempt to discuss the subject
in detail. What I intend to provide is an introduction to the
subject that helps you understand its importance and makes you
want to learn more.
Because Java is a purely object-oriented language, the process
of designing applications using Java follows well-established
object-oriented software engineering methodologies. A methodology
is simply a step-by-step process for performing a task; at each
step along the way, you document what you did. An object-oriented
methodology specifically looks at the system as an interplay of
objects and thus seeks to understand the objects that make up
that system and how they interact with each other.
Any software engineering process can be cleanly divided into six
stages:
- Analysis
- Design
- Development
- Testing
- Implementation
- Maintenance
These steps proceed iteratively, meaning that once a subsystem
comes out of testing it probably will go back into development
(or sometimes even design). This loop will repeat as many times
as necessary until all testing requirements are met.
Traditionally, 80 percent of software development is spent in
the last stage, maintenance. In other words, the most effort on
a system is spent fixing bugs and correcting things done poorly
in the first five stages. Because getting something right the
first time is always less costly than going back and redoing it,
the goal of object-oriented software engineering is to shift time
back to the first and second stages so that much less time is
spent in the final stage.
Analysis
Before you begin any software development project, you first have
to analyze the problem and understand what it is. The task of
analysis is to prioritize system components by classifying them
as either wants or needs. You must be able to accommodate the
proper amount of time for the design and development of your system's
needs. If you still have time on your planning chart after the
needs have been accounted for, you then note which wants
you will accommodate and which ones you will not. If you plan
to implement a want in a future release, you should note that
in your documentation so that designers can plan for that future
expansion.
Note |
This chapter offers a very distilled object-oriented software engineering process geared more towards the casual or small-project developer. There are actually several major object-oriented approaches in common use today. This chapter takes the major components of the well-known methodologies and describes their essence. Regardless of what sort of projects you are involved with, I highly recommend that you familiarize yourself with at least two of the major methodologies.
Furthermore, if you are developing software professionally for a large audience, I strongly advise that you follow one of those methodologies for two reasons: First, a well-known methodology provides a common ground of understanding for a diverse set of people-from entry-level developers to senior architects and managers. Second, whenever you tell a potential customer that you follow object-oriented design practices, their first question, oddly enough, will be "Which methodology do you use?"
|
The documentation of your analysis can be fairly simple. It only
has to show which pieces of your system will be considered in
scope, that is, what will be built in the first release. For
everything out of scope, you should note why it is out of scope
and whether you plan to address it in a later release.
Tip |
If you are building an application for a specific group of people, this is the best time to show them your analysis document and have them sign it. If, at a later date, they come back to you, asking why feature X was not in the system, you can show them their approval of the document.
|
The following outline captures the essence of what an analysis
document should look like:
I. General Use Cases
II. Context Model
III. Notes
To capture the proper wants and needs, you should sit down with
potential users and document general use cases, sometimes
called scenarios. A general use case is a simple statement
describing a function of the system. For example, if I were building
a program that predicted football games, I might have a use case
like this:
The user requests a prediction for two teams.
Your use cases will help you identify high-level components that
you can place into a diagram to graphically illustrate which pieces
of the application are in scope and which are out of scope. This
diagram is called a context model. Figure 29.1 shows a
sample context model for the football game prediction program.
Figure 29.1 : The context model for a football game prediction program.
The content of a context model is simple. You design and build
anything inside the box. Anything outside the box is out of scope.
If there is a line connecting something inside the box with something
outside the box, that means that your system is interfacing with
an external system.
The last section of your analysis document should briefly describe
any decisions you made about the scope of the application. Specifically,
you want to record anything you cut from scope and why you cut
it.
Design
By understanding the problem, you can go on to design a solution.
During the design process, you break the application into objects
and provide descriptions of how they interact to satisfy the general
use cases in the analysis document. The end product of your design
should be a document that tells you exactly how to build your
system without diving into coding details. The outline of a good
design document should look something like this:
I. Object Model
II. Detailed Use Cases
III. Object Specifications
To put together the object model, you first have to identify all
your objects. Identifying objects is a lot less complex than it
sounds. A few people gather around a white board or a piece of
paper and name things they think are objects straight off the
top of their head. At first, there are no wrong or right answers.
Later, as you start applying the objects to use cases, you will
see which objects best describe the system and then eliminate
from your design those that do not.
The following list of objects comes from a brainstorm session
on the design for the football program:
- Team
- Statistic
- Game
- User
- Points-for
- Points-against
- File
Once you feel comfortable with the initial list of objects, the
next step is to attempt to identify recurring patterns within
those objects. The simplest form is that of inheritance, in which
one object is simply an extension of another object. Clearly,
the points-for and points-against objects are really extensions
of a statistic. But is a statistic really a simple attribute of
a team? Or is it a more complex object with independent behaviors?
Because converting an object into a simple data type is easier
than converting a simple data type into an object during the design
process, you should keep anything as an object until you are certain
it is not.
Completing the design involves iterating over the process of fitting
objects into use cases, fleshing out those use cases, and then
identifying opportunities for abstraction until you feel you have
a clearly defined architecture for the development of your system.
To reach this stage, you need documentation identifying every
single object you will build for the system, its attributes, and
its behaviors.
The object model is a diagram that illustrates your understanding
of the system as a whole. For a small system like the football
game prediction program, the diagram can be as simple as the one
in Figure 29.2.
Figure 29.2 : The object model for the football game predictor.
This diagram shows you exactly what you have to code. All that
is left to figure out is how to code it. In object-oriented programming,
an application consists of objects that are made up of methods
and attributes. After putting together the object model, you next
must detail each step of the general use cases documented in the
analysis documentation. By going into deep detail on the use cases,
you can see each thing that a given object does and the data it
requires to support that behavior. As you did with the general
use cases, you provide simple English sentences that describe
what happens. The general use case, "The user requests a
prediction for two teams," might be divided into the following
detailed scenario:
- The user executes the program with the two teams as arguments.
- The prediction object
creates a team instance for
each of the teams.
- The prediction object
requests relevant statistics from each team.
- The prediction object
calculates an outcome.
- The prediction object
displays the results to the user.
From this detailed use case, you can see that the team
object will need methods for retrieving statistics, and the prediction
object will need methods to instantiate team
objects and predict games.
The final important section of a design document is a list of
objects with their attributes and methods, called the object
specification. Figure 29.3 shows the object specification
for the Team object in the
football predictor.
Figure 29.3 : The object specification for a Team object.
Into Development and Beyond
The final stages of the software engineering process form the
traditional bulk of where work is done. During development,
you write the code that becomes the system. As you complete each
module or subsystem, you test it to make sure it works.
For the purposes of this chapter, testing is simply making sure
that each subsystem does exactly what your design documents say
it should do. In a very formal environment, however, the testing
process is a very complex subject about which entire books have
been written.
Once the application has been thoroughly tested and you know it
does everything it is supposed to do, you release the code to
the people who will actually use it. Using Java's ability to create
zero-install applications, however, the time you spend in the
implementation stage is greatly reduced. You make the classes
available over the Web and let people run the application. The
rest of the implementation stage is spent gathering feedback on
how the application performs in the real world so that you can
prepare and manage the maintenance stage.
After you first release a product, chances are you will want to
enhance it and fix any bugs you didn't catch in testing. Perhaps
you may even want to reengineer portions of it as your development
experience grows. The final phase of software development, maintenance,
is about doing just that. Of course, the creation of a new release
is a full exercise in software development in itself. Maintenance
thus brings you full circle back into the analysis stage of software
development.
The documentation created in the analysis and design phases provides
the prescriptive documentation you need to build an application.
During the iterative process of systems development, however,
you will encounter issues that you simply missed in analysis and
design. You therefore need a way to communicate to others exactly
how your system was built.
Developers commonly write their descriptive documentation from
memory after an application is built. They then fail to maintain
the documentation along with the application. For someone trying
to make a change to an application using poorly maintained documentation,
no documentation is almost as good as what they have to work with.
Fortunately, Java provides the javadoc
utility for documenting your code as you write it.
The javadoc
Utility
The javadoc utility is a
small Java program that comes with the JDK to help you create
object documentation straight from your code. javadoc
uses a combination of compiler information and comments made by
you to build HTML documentation that describes everything about
a Java object. As long as you maintain your comments with your
code, your application documentation always reflects the current
state of its objects.
If you have visited the JavaSoft API documentation pages at http://java.sun.com:80/products/JDK/CurrentRelease/api/,
you have seen the kind of descriptive documentation produced by
javadoc. To produce Web pages
that properly document your objects, you must include the following
in each object source file:
- A description of the object
- A description of all public,
protected, or private
protected attributes
- A description of all public,
protected, or private
protected methods
Each description appears in Java code as a comment just before
the element it is describing. To mark the comment as a javadoc
comment, it must be in the format:
/** description */
Listing 29.1 shows what the Team
class source code looks like.
Listing 29.1. The Team
class source file, with javadoc
comments.
/**
* The Team class has statistics and receives updates from users.
* It represents one football team and maintains the statistics for
* that team.
* @version 1.0
* @author George Reese
*/
public class Team {
private TeamFile file;
private int wins, losses, points_for, points_against;
/**
* Constructs a new Team object based on a team name.
* Specifically, it creates a TeamFile object to access
* the file in which team data is stored. It then uses
* the file object to get all of the team's stats
* from storage.
* @exception FileException caused by a failure to read storage file
* @param name the name of the team
* @see TeamFile#getField
*/
public Team(String name) {
file = new TeamFile(name);
wins = f.getField("wins");
losses = f.getField("losses");
points_for = f.getField("points_for");
points_against = f.getField("points_against");
}
/**
* Adds the statistics for a recently completed game.
* In this release, we are ignoring ties.
* @exception FileException thrown by a failed attempt to save the file
* @param pf the points this team scored in the game
* @param pa the points scored by the opponent
* @see TeamFile#setField
* @see TeamFile#save
*/
public void addGame(int pf, int pa) {
if( pf > pa ) wins++;
else if( pa > pf ) losses++;
points_for += pf;
points_against += pa;
file.setField("wins", wins);
file.setField("losses", losses);
file.setField("points_for", points_for);
file.setField("points_against");
file.save();
}
/**
* Allows the Prediction class to get team statistics
* for this team.
* @exception NoSuchStatException thrown if a bad stat is requested
* @param stat the desired stat
* @return the value for the requested stat
*/
protected int getStat(String stat) {
if( stat.equals("wins") ) {
return wins;
}
else if( stat.equals("losses") ) {
return losses;
}
else if( stat.equals("points_for") ) {
return points_for;
}
else if( stat.equals("points_against") ) {
return points_against;
}
throw new NoSuchStatException(stat);
}
}
Ideally, you should be commenting your code to this degree anyway.
Why not have those comments help you get your documentation done?
You may have noticed some fields in the comments beginning with
an @ symbol. The javadoc
utility enables you to pass certain information using these @
keywords. In the Team class
example, I used @version,
@author, @exception,
@param, @return,
and @see to tell javadoc
about the class version, class author, method exceptions, method
parameters, method return values, and other references.
Table 29.1 lists all the keywords you can use in your javadoc
comments with their meanings.
Table 29.1. Keywords used by javadoc.
Keyword | Description
|
@author
| Used in class comments. Identifies who wrote the class. You can have multiple @author tags for a class.
|
@exception
| Used in method comments. Identifies the full class name for any exceptions thrown by the method. You can have multiple @exception tags for a method.
|
@param
| Used in method comments. Identifies a method parameter. You should specify as many of these as the method has parameters.
|
@return
| Used in method comments. Describes what the method will return.
|
@see |
Used in any comment type. Allows you to reference other classes, attributes, and methods with a hypertext link.
|
@version
| Used in class comments. Identifies the version number for this class.
|
Among the advantages of this style of documentation is that you
can embed HTML tags inside your comments that will appear in your
final HTML documentation. You can even go so far as to embed applets
inside your applet documentation! More importantly, this kind
of documentation enables you to create links to related documentation
that is beyond the ability of the @see
keyword to handle. The only limitation is that you cannot embed
a <HR> or <H1>
through <H6> HTML tag
inside a comment. Although doing so does not generate an error,
it may produce unexpected results in your output documentation.
Once your source files are complete, both in terms of code and
commenting, you are ready to build the documentation for the application.
To do this, you simply execute the javadoc
command with the class file as an argument. The javadoc
command creates five output HTML files with documentation for
your class. The generated files are listed here:
- YourClassName.html -Detailed
documentation for your class.
- Package-YourPackageName.html-For
each package, one of these files is created listing all the classes
in that package.
- AllNames.html-A list
of attributes and methods for all classes (in this case, just
one class) in alphabetical order.
- packages.html-A list
of all packages in your application.
- tree.html-A list of all
classes, shown as an inheritance tree.
Of course, you probably want to document more than one class at
a time, and you probably do not want that documentation stuck
in the same directory as your source code. The javadoc
command accepts a host of options so that you can customize your
documentation to your needs. Before you worry about those options,
however, you should understand what Java does count on. It expects
a directory tree that ships with the JDK, containing stock graphics
as well as the Java API documentation (javadoc
produces documentation that contains appropriate links to Java
API documentation).
Managing links to existing documentation is one place in which
javadoc gets a little messy.
Unfortunately, javadoc likes
to pile all your documentation into one huge directory. Additionally,
javadoc is not smart enough
to determine any class dependencies on its own. If you are generating
documentation for classes with dependencies, you must tell javadoc
so that it regenerates any old documentation. Finally, the HTML
files created by javadoc
expect an images subdirectory
under the directory in which they are located. You can generally
take care of this problem by creating a link to the images
directory that came with the JDK.
On the command line, javadoc
can take one of two forms:
- javadoc [options] file
- javadoc [options] package
You can customize javadoc
output with the options listed in Table 29.2.
Table 29.2. Options for use with javadoc.
Option | Comment
|
-author
| If there are @author tags in the comments, list them in the documentation.
|
-authors
| Same as the -author option.
|
-classpath path list
| Manually sets the class path for finding source code. Remember that your CLASS PATH environment variable is generally set to where your .class files are located, so you will almost always have to specify this option.
|
-d directory
| The directory in which the generated documentation should be placed.
|
-depend package list
| A list of dependencies for the package being documented.
|
-version
| As with the @author tag, you must specify that version information should be included in the documentation.
|
-verbose
| Print information about the progress of javadoc as it is generating documentation.
|
Although generating documentation may not be the most enjoyable
part of software development, it is a necessity. Successful applications
built in a timely fashion with small maintenance requirements
invariably are well-documented applications flowing from a proven
engineering methodology. The analysis and design stages of development
provide you with prescriptive documentation that tells you exactly
what has to be built before you build it. In the development stage,
you can use the javadoc utility
to create up-to-date descriptive documentation that helps people
maintaining and modifying your code down the road-even if you
are the one doing the maintenance and modification.
Contact
reference@developer.com with questions or comments.
Copyright 1998
EarthWeb Inc., All rights reserved.
PLEASE READ THE ACCEPTABLE USAGE STATEMENT.
Copyright 1998 Macmillan Computer Publishing. All rights reserved.