Edited and maintained by Bryan Quinn.
A good developer knows that there is more to development than programming. A great developer knows that there is more to development than development. (Ambler, 1999)A recurring problem encountered by developers working on software projects is the maintenance of legacy code. When you first write a piece of code, its contract and implementation may be clear to you, but it is likely that it is not clear to anyone else. To solve this problem, professional programmers adhere to standards. The application of a standard ensures that the APIs and implementations of your program are clear to anyone familiar with the standard. This not only helps others use your code, but will assist you when you to understand what the code does at a later date.
Working in Java can increase this problem because it can be sometimes be easier to write code quickly in Java than in other compiled langauges like C. It is best to sit down, away from the computer, and think about the APIs and requirements for your code before implementing it.
If you're over 78 columns in one line, you may be editing a long String. In Java, you need to use the concatenation operator (+) to join together Strings on multiple lines:
String myLongString = "This is a very long string that goes on" +
"for a considerable amount of time before finally stopping\n.";
M-x column-number-mode
\$Id\$ header
for use by CVS. CVS will expand this header and identify the
last time someone touched the file and print their CVS login
name. All Java class files should have a final static
String member called cvsId. The identifier
can be accessed with the ident program to identify
the version of a compiled class file.
If your program isn't worth documenting, it probably isn't worth runnning. (Nagler, 1995).
In order for someone to use a piece of code that you have written, it must be carefully documented. You may feel that your class is not important enough to explain or that its use is self-evident from the code itself. If that is so, then it is highly questionable that your class provides a useful abstraction or that it is useful towards any end. When you write some code, it is because you are attempting to solve a problem, but your solution is not accessible unless there is some documentation to describe it.
Some people challenge this by saying that clear code requires no documentation. To some extent, this is true. Certainly, clear code requires less documentation than complex code. It is also preferrable to simplify complex code and then document it, rather than simply document the complexities. However, Nagler raises a real point in the quote that opens this section: no matter how clear a piece of code is, the comments describing it will be more instructive, particularly to someone trying to understand your code quickly.
Documentation goes above and beyond the level of code. You should always consider writing documentation and diagrams that explain the requirements, design, and use of any code that you right and include links to such material in the code itself.
Fortunately, Java provides its own self-documenting language for code, Javadoc. The documentation system for Tcl procedures in ACS 3.4.x and above included a Javadoc-like documentation facility that could be accessed through an API Documentation browser. ACS Java only provides the Javadoc generated documentation for procedures, accessible at the same URL, /api-doc. Unlike the Tcl version, Javadoc must be explicitly invoked in order for the documentation to be rebuilt.
Javadoc provides a number of directives to add documentation to classes and methods. All classes should contain a comment that explains:
For methods, at a minimum, whether it is public or private, should contain documentation that explains the contract or responsibilities of the function. This contract should contain:/** * This is a simple HelloWorld program to illustrate the ACS * Engineering Standards. * * Adapted from Davis, M. (2000) "Incremental development with Ant and JUnit" * http://www-4.ibm.com/software/developer/library/j-ant/index.html * * @author Bryan Quinn * @version 1.2 2001/01/21 01:49:34 * @since ACS 4.0 */
In addition to the description of the contract, all parameters should
be described with @param and the information being
returned (if applicable) should be documented with @return.
/**
* A Java Application must contain a static main method which is executed
* when the application starts. This application simply prints
* Hello World to the console.
*
* @param args a <code>String[]</code> value; this application ignores any arguments passed.
*/
public static void main ( String [] args ) {
HelloWorld helloWorld = new HelloWorld();
System.out.println(helloWorld.sayHello());
}
/**
* This method returns the string "Hello World".
*
* @return The string "Hello World"
*/
public String sayHello() {
return ("Hello World");
}
See How
to Write Doc Comments for Javadoc for full information on how to
effectively use Javadoc.
"What's the use of their having names," the Gnat said, "if they won't answer to them?"The importance of names is often overlooked by programmers. However, from the perspective of reading and maintaining code, transparent variable names are crucial. The ability to simply skim a line of code and identify what is occurring depends on a clear name that describes what the function does.
"No use to THEM," said Alice, "but it's useful to the people who name them, I suppose. If not, why do things have names at all?" Lewis Caroll, Through the Looking Glass
A key benefit to descriptive naming is that it establishes a conceptual consistency to your work. For example, the Java 2 Core APIs follow consistent standards for the names of methods of functions. When one becomes familiar with one piece of the core APIs, the knowledge of the syntax of the APIs encoded in its naming transfers over to consideration of other APIs. This makes it much easier for developers to get up to speed on the Java APIs. The Java APIs were designed well, and many programs written in Java also strive to follow these conventions. We have the same goal for the ACS. If you strive to follow this example, you can produce code that other developers will adopt with ease and your code will be read and reused with ease. To be a successful open-source program, the code must meet this standard.
Class names should begin with a capital letter and contain a noun. The rest of the name should be lowercase, but because Java does not allow spaces, denote additional words with a capital letter, e.g. SqlFragment.
Method names should begin with a lowercase letter and obey the standard capitalization rule for additional words. Method names should be of the form verbObject, where the object is optional. For example, toString() and getValue().
Field names can be flexibly defined, but should be easy to understand. Stay over from overly short names, such as int i or vague names like selection. Field names should begin with a lowercase letter and obey the standard capitalization rule, e.g. acsForm and httpRequest. Other tips:
User, but one might be in
the com.arsdigita.acs package, and the other might be in
the java.security package. Because the classes are
separated into packages, there is no conflict if the same program uses
both of them.
| Package | Purpose |
|---|---|
| com.arsdigita.acs | The central ACS classes. |
| com.arsdigita.acs.db | The database API. |
| com.arsdigita.acs.* | Any package that is part of the core, such as ACS Administration,
should get its own Java package for its class files, for example, com.arsdigita.acs.acs-admin. |
| com.arsdigita.* | Any package that is not part of the core, such as BBoard, should
get its own package, for example
com.arsdigita.bboard. However, if the package is not
being maintained by ArsDigita, the com.arsdigita
should be replaced by something more appropriate. |
package com.arsdigita.acs;
/**
* What the class does.
*
* @author <a href="mailto:bquinn@arsdigita.com">Bryan Quinn</a>
* @version 1.2 2001/01/21 01:49:34
* @since ACS 4.0
*/
public class New {
public final static String cvsId = "java.html,v 1.2 2001/01/21 01:49:34 bquinn Exp";
}
AJP file. Note that
it includes identification of the author, creation date, and cvs-id.
<%@ include file="/acs.jspi" %>
<%
ad_page_contract {
Document the file's purpose here.
@author Bryan Quinn (bquinn@arsdigita.com)
@creation-date 10/10/2000
@cvs-id java.html,v 1.2 2001/01/21 01:49:34 bquinn Exp
} {
} -properties {
}
// Insert code here.
ad_return_template
%>
Here is an example of a class. The document will refer to this example when describing reccomendations for working with classes.
class Foo {
private int foo, bar; 2 private fields.
private String baz; Another private field, a String.
public Foo () { A public constructor.
foo = 4;
bar = 5;
}
public String getBaz() { A public accessor procedure.
return baz;
}
public void setBaz(String baz) {A public mutator function.
this.baz = baz;
}
public int getNum(boolean b) { A public accessor function with some logic.
if (b == 1) {
return foo;
} else {
return bar;
}
}
}
Classes and their members can all be modified with a visibility modifier to change the visibility of the class and the member. A public class is accessible anywhere, and a public member of that class is also accessible anywhere. However, a number of restrictions are available as outlined in the table below. The package-friendly modifier is the default if there is no modifier at all. However, you almost never want to use it.
| Accessible to: | public | protected | package (default) | private |
|---|---|---|---|---|
| Same class | yes | yes | yes | yes |
| Class in same package | yes | yes | yes | no |
| Subclass in different package | yes | yes | no | no |
| Non-subclass, different package | yes | no | no | no |
When writing a class, you usually begin by typing
public class Name (extends Name {
....
}
This defines a class of name Name that should
also be in a file called Name.java. A public
class can be instantiated by anyone. You can also make a class
protected or package-friendly when
appropriate. You may find it helpful to make a class package
friendly if it will only be used by a public class in the same
file. Java allows you to put multiple package-friendly classes
in the same file as a public class.
In most cases, you should make fields private. A private field can only be accessed by methods within the class, but not outside from the class. This creates a barrier separating the users of the class from the implementation. You can change the logic by which a field is set or accessed without affecting the interface. Fields should only be manipulated via public accessors and mutators (discussed below).
The only time you should make a field public,
is if it is a static field intended to
encapsulate a constant for use by other classes. A good example
of this is Boolean.TRUE. A static field can be
accessed without instantiating a class, but simply by typing the
class name . field name.
Under extremely limited circumstances, you may want to make a field protected. A protected field can be modified by subclasses. This is useful if you want programmers extending your class to be able to extend the implementation. However, you should think carefully before using this and carefully document the reasons. Anyone extending your implementation may choose to change something that your implementation needs to maintain control over. If this occurs, then the implementations can become incompatibile resulting in compiler errors, data corruption, and system failures.
To make your code safe to extend, think of your class as providing a base interface and implementation. Someone who extends your class wants to continue to offer the same interface and implementation present, but add some methods that build from the base class and extends its interface and implementation. These extensions should not interfere or alter the base class functionality, but use it as building block.
The base class is the first building block at the bottom of a tower. Extensions of the class are additional blocks put on top. The top blocks rely on the bottom blocks for stability and essentials such as a foundation. The bottom blocks rely on the top blocks to not topple the building.
Fields are invariants that represent the stateful data of a class. Allowing programmers to access them directly prevents you from maintaining an interface and increases the risk of system failures.
Methods provide an interface for other classes to access the data and logic that your class provides. Two types of methods are quite common and are used to provide an abstraction barrier around fields: accessors and mutators.
Accessors are used to obtain the data stored in the fields of the class. The naming convention is typically getFieldName. In many cases, no arguments are necessary, so the accessor often looks like
public FieldType getFieldName() {
return fieldName;
}
However, accessors are often customized to do some kind of
transformation of the data represented in the field into a type
suitable to the class accessing the data. It is also customary
to add optional parameters to accessors to alter how the
transformation takes place.
Mutators are used to change the value of fields in the class.
The naming convention is typically
setFieldName. They always take one argument,
sometimes several, and usually have a void return
type. For example:
public void setFieldName(FieldType fieldName) {
this.fieldName = fieldName;
}
Methods are public in the case when you want any other class to be able to get access the logic in the class. Methods can also be restricted as appropriate, and this includes the constructor. For example, the ACS contains a class that represents a HTTP request to the server. This class has a protected constructor that only the ACSRequestFactory class can call. This enforces a particular process flow for this class and increases maintainabily. If any additional programming statements need to be performed before calling the constructor request, they can be enclosed in the Factory's methods.
/**
* Protected constructor. Should only be called by factory class.
* @param requestedUrl the original, unresolved abstract URL requested
* by the user
*/
protected ACSRequest(String requestedUrl) {
this.requestedUrl = requestedUrl;
this.siteNode = SiteNodeMap.getSiteNode(requestedUrl);
if (siteNode != null) {
pkg = siteNode.getPackage();
packageUrl = siteNode.getUrl();
// make sure to include leading slash
extraUrl = requestedUrl.substring(packageUrl.length() - 1);
}
this.extraPathInfo = "";
}
Java allows for method to be overloaded. Overloading a function means having multiple methods with the same name, but different signatures. A different signature means different arguments. Method overloading enables one to create a more flexible interface for a function that allows for related functionality to use the same name. You should consider using this feature as it significantly aid in the clarity of code.
You cannot overload a method by chaning only its return type. If you want to create an overloaded a method with a different return type of the method, you must provide a method with a different arguments.
When you go against a standard, document it.The above comprise a set of guidelines that the ACS Java team has constructed to make our own work more efficient and useful. However, it can't possibly cover every case and there are always occasions when the guidelines above should be overriden. When doing so, please document why, so that readers of the code can understand why the change was necessary. However, the usual causes of broken standards is deadline pressure. One response to this is to maintain a professional attitude towards your work.
(Ambler, 1999)
Programmers believe in the necessity for high standards of code and often cite deadline pressure and tight schedules as impediments to adopting it. The first response you can make when forced into such a situation is to explain a business case for the necessity for documentation and an adjustment of the schedule:
The key message is that software quality requires time, but it is time well spent. Be candid about the need for such time when making estimates about your schedule.
In summary, here a few general principles towards keeping a professional attitude towards your code:
| Document Revision # | Action Taken, Notes | When? | By Whom? |
|---|---|---|---|
| 0.1 | Creation | 11/20/2000 | Bryan Quinn |