by George Reese
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.
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 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 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:
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.
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.
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:
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:
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.
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 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:
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.
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:
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:
You can customize javadoc
output with the options listed in Table 29.2.
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.