Java 1.1 Unleashed
- 29 -
Documenting Your Code
by George Reese
IN THIS CHAPTER
- The Role of Documentation
- The Software Engineering Process
- Object-Oriented Design
- Documenting Code
If one single thing can define the difference between hackers and serious programmers,
it is documentation. The trademark of a hacker is a quick-and-dirty solution developed
without the use of 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 is worlds above the quality
of applications built by hackers.
This chapter examines the role of documentation in object-oriented application
development. It focuses on approaches to object-oriented development that help produce
useful documentation and on the javadoc tool, which creates API documentation
from comments in your source code.
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 flows
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.
The Software Engineering Process
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.
Let's start out by taking a brief look at object-oriented software engineering
and how it relates to the documentation you produce. I first want to take a look
at object-oriented software engineering; keep in mind, however, that many volumes
have been written on the subject, so I cannot possibly cover all its nuances in this
short chapter. Instead, I want to provide an introduction to the subject to help
you understand its importance and whet your appetite for more.
Object-Oriented Design
Because Java is a purely object-oriented language, the process of designing applications
using Java should follow 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--object-oriented or otherwise--can be cleanly
divided into six stages:
- 1. Analysis
2. Design
3. Development
4. Testing
5. Implementation
6. Maintenance
These steps proceed iteratively, meaning that once a subsystem comes out of testing,
it probably goes back into development (or even, sometimes, back into design). This
loop repeats 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 decision 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 these 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,
such as OMT and the new Universal Methodology.
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 included 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 can 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.
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.
Figure 29.1.
The context model for a football game prediction program.
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 heads. 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 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 that identifies 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 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 detailed in the analysis
documentation. By going into deep detail on the use cases, you can visualize the
things each 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," can be divided into the following detailed scenario:
- 1. The user executes the program with the two teams as arguments.
2. The prediction object creates a team instance for each of
the teams.
3. The prediction object requests relevant statistics from each
team instance.
4. The prediction object calculates an outcome.
5. The prediction object displays the results to the user.
From this detailed-use case, you can see that the team object requires
methods for retrieving statistics and the prediction object requires 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 that 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 Internet or intranet and let people run the application. The rest of 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.
Documenting Code
The documentation created in the analysis and design phases comprises 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 that has poorly
maintained documentation, no documentation is almost as good as what he or she has
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 when you use the javadoc utility.
If you have visited the JavaSoft API documentation pages at http://java.sun.com/products/JDK/CurrentRelease/api/,
you have seen the kind of descriptive documentation produced by javadoc.
To produce HTML 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 this
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,
along 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 any 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 an <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, 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:
Filename |
Description |
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 classpath for finding source code. Remember that your CLASSPATH
environment variable is generally set to where your .class files are located,
so you 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. |
Summary
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 maintain
and modify your code down the road--even if you are the one doing the maintenance
and modification.
|