http://www.developer.com/java/ent/article.php/3346291/Can-IDEs-Do-More-to-Improve-Code-Quality-Part-II.htm
In our previous article, we looked at IDE tools for the creation of documentation in the source code. This time, we are going to talk about comment maintenance, among which synchronization and code conventions are major issues. Then, we introduce some custom Javadoc tags and their uses. Finally, we look at miscellaneous uses of comments. In this section, we discuss aspects and tools that ease the task of working with an existing code base. We are first going to define an important term that will be used throughout this part. Refactoring is the part of code maintenance that doesn't fix bugs or add new functionality. Rather, it is designed to improve the understandability of the code or change its structure and design, to make it easier for human maintenance in the future. Code conventions are non-absolute rules defined by a group of people. The objective of those rules is to reduce confusion among the group. One can distinguish naming conventions and formatting conventions. A good IDE removes formatting from the programmer's responsibility. Ideally, one wants incremental formatting when saving a source file. Except from defining the format to use, the programmer is not distracted by the job of formatting and can focus on writing code. In Eclipse, one has to format the source code manually by executing the "Source Format" action. This formats the source code according to format preferences. One can find them in "Window -> Preferences; Java -> Code Style -> Code Formatter." In the Code Formatter, one can define a wealth of variables, from lines breaks to braces positions. Formatting conventions are a very small part of a bigger picture. Indeed, one can glimpse the pattern that was already introduced in Part I: The IDE now supports the developer by implementing checkable aspects of his methodology. In Eclipse, the developer can define prefixes and suffixes for the following variable types: For example, there is a growing trend in the Java community to prefix private fields with an underscore. One also usually declares constants with capital letters: The aim is to increase code readability. Eclipse does not fully implement this aspect. Indeed, Eclipse only uses the above prefixes and suffixes for generated code. Eclipse does not generate a warning for source code that doesn't comply with the prefix convention. However, plug-ins such as "Checkstyle" (see the Resources section) exist to enforce such naming conventions. For methods, by looking at the name, one should guess what the method is supposed to do. It is important to strike a balance between conciseness and strength of meaning. For variable identifiers, the previous argument applies as well. However, it is quite handy to use short variable names. Indeed, many people find such code easier to read. But, other people like longer names better, so how does one solve this dilemma? One solution lies in renaming variables on the fly when needed. However, this is only imaginable for closed-world variables, variables whose uses are all known to the IDE. Thus, based on meta-data indicating how to construct an identifier, the IDE refactors the code on the fly for the following: One could possibly do that for package identifiers as well. However, for big packages the operation becomes time consuming. Protected and public are API variables (i.e. open-world variables). Renaming such variables constitutes an API change and so potentially breaks client code. Once comments are written, one wants them to stay synchronized with the code. In other words, when a change is made in the source code, the comments must be updated accordingly. When a comment is not synchronized, it loses its meaning. If someone decides to change the name D to R without updating the comment, it becomes hard to use. Occurrences of D in the comment might point either to F, G, or the new R. Not so long ago, when you wanted to do a task such as renaming a class or a variable identifier, you had to do it manually. That was error-prone and time consuming. Today, one should use refactoring operations that enforce code and comments synchronization. In Eclipse, when the insertion point is on an identifier, one may rename it with the action "Refactor-> Rename". The IDE automatically updates the source code in all relevant files. Nevertheless, Eclipse does not synchronize references in comments. It is a hard problem indeed to solve with the current comment language. Here is a simple illustration of the problem: In the preceding Javadoc comment, how can the IDE know which occurrence of "name" refers to the parameter? Our brain knows it because it understands a complex language. In "The car's name is now name", Eclipse sees text with no special meaning. String equality as a criterion is not acceptable, because comment text could then become "The car's aliceName is now aliceName". One has to use a syntactic construct that enables the IDE to distinguish between template text and normal text. In the Javadoc paradigm, one could define an inline Javadoc tag for variables. With the extended Javadoc definition, the refactoring tools are now able to synchronize comments correctly. Nevertheless, such new syntactic conventions can be tedious to use. Eventually, there is a need for designing ergonomic tools that diminish the overhead generated by those notational conventions. As discussed in Part I, the overhead generated by the {@var } construct can be diminished with the Code Completion feature. The inline @var tag is not officially supported. One needs an IDE supporting custom Javadoc tags. A few IDEs fully support custom Javadoc tags. The current Eclipse milestone is not among those. One can expect custom tags support in the near future, though. Custom tags enable developers to more tightly integrate the use of design by contract concepts; the most important ones are pre conditions, post conditions, and class invariants. A condition is an expression whose result is a Boolean value. Conditions are applied to Java methods in the following way: In the current official Javadoc tool, there are no pre-condition and post-condition tags. That information, if not missing, is often spread over the whole Javadoc comment. In the official setup, one can write anything because there are no templates to follow after the tag name. The IDE is a possible place for defining an extended documentation language and enforce its syntax rules. The main benefit of extending the documentation language is a better specification structure that: Class invariants express general consistency constraints that apply to each instance as a whole. Invariants are conditions that must hold: This section describes a mechanism that can reduce occurrences of the ubiquitous NullPointerException in Java. The following code defines the Car class implementing the IWeighable interface. One can see two custom tags. The @def tag gives a natural language description of the _ownerName field. The @inv tag defines a class invariant on the _ownerName field. Provided the IDE is aware of the meaning of those tags, it can take charge of many tasks and automatically report problems to the user. If one declares the following constructor: An IDE is able to generate a rich skeleton: Let's further illustrate with the following method: Again, most of the documenting job can be automated and the IDE will write the following skeleton: The real useful feature kicks in when one is using the Car class as shown here: This code snippet above compiles normally without errors. It also runs without any exception being thrown. Indeed, that's normal from the Java Language Specification (see the Resources section). However, from the designer viewpoint, the programmer is making an error; he is breaking a pre-condition by calling the setOwnerName method with a null value. It is thus desirable for the IDE to generate an error and warn the user. One could use an Exception scheme, throwing a ConditionNotMetException when pre-conditions are not respected. But that would only offer runtime error checking and still generate those custom exceptions defined by the scheme. Error reporting at compile time is much better. Three possibilities are possible to make this kind of tool a reality: When one is sub typing by implementing an interface or extending an existing class, it is a good practice to follow the Liskov Substitution Principle. Barbara Liskov first wrote it as follows: Stated more simply, programs using references to base types must be able to use references to derived types without knowing the difference. In Java, this principle fits especially well when one is using the pre-conditions and post-conditions constructs that were introduced in the previous section. In this paradigm, the principle easily translates into: On a synchronized method specification, one may Let's show an illustration of the rule of thumb by taking back the method definition of the IWeighable interface; pre-conditions and post-conditions have been integrated. From the viewpoint of the person who implements this interface, the comment cannot be changed in IWeighable.java. Let's synchronize the Javadoc comment and see what can be changed in Car.java; After implementing the method, the developer might then strengthen the post-conditions by adding @post w >= 1, because any instance of his Car implementation always carries a weight above zero. From the IWeighable perspective, this new post-condition does not change the contract. The LSP is respected. Indeed, when w >= 1, one also has w >= 0. Is it be possible to remove the pre-condition @pre unit != null? The answer is no. Without this condition, one cannot fulfill the contract defined in the @return tag that states the returned value must be expressed in the units defined by the variable unit parameter. Not respecting such constraint is the kind of mistake that leads Mars probes to crash unexpectedly. What about exceptions and rules one shall follow? Based on the getWeight definition in the IWeighable interface, for an implementation of getWeight in Java, one Clearly, when one is modifying a Javadoc comment according to the LSP, one needs the whole specification under his eyes to avoid mistakes. A complete Javadoc comment synchronization tool is thus needed, a tool that would help the developer at enforcing the LSP. Such a tool does not yet exist for the Eclipse IDE, but can be expected in the future as a plug-in. Changes in interface specifications are rare. However, when changes do happen, one is supposed to iterate through all interface implementations to check the compliance with the new specifications, and make modifications if necessary. This process is not necessary if the specification is changed in such a way as to make it more general. One makes a specification more general by adding a pre-condition or removing a post-condition. In the current state of affairs, Eclipse does nothing when one modifies the Javadoc comment in an interface. Automatically generating todo tasks is a useful improvement to ease the maintenance work. Such a task may include information such as the modification date and the interface in which the modification was made. We have seen how to write and maintain good documentation in the source code. However, it would be pointless to make these processes efficient if there were no productive ways to benefit from this documentation. Here are a few examples of what is possible to do in the current version of Eclipse. Indeed, with minimum time wasted, Eclipse can fetch relevant documentation and display it in a non-obtrusive popup window. Take, for example, the following code snippet: To display the specification of the setOwnerName method, one puts the insertion point inside the method call setOwnerName and presses the F2 key. One also can hover the mouse cursor over the method name for half a second. Easy access to information allows one to make sure the objects and methods are used correctly. The IDE also uses those comments to generate API documentation in HTML. A browser can be started from inside the source editor for displaying this documentation. In Eclipse, the action in menu Navigate -> Open External Javadoc will show the API documentation relevant to the object selected. Comments take space in the editor. Thus, once one knows a class pretty well it is legitimate to hide the comments by folding them. Code folding is scheduled for the next Eclipse release in June 2004. One can try to improve the feature by defining behavioral preferences enabling the IDE to know when to fold or unfold a comment. For example, one may tell the IDE to fold comments after two visits. The goal of an IDE is to help developers. The IDE is especially useful for undertaking dull and easily automated tasks. It can do straightforward operations in the background while letting one focus on interesting things. We have seen what already exists in the Eclipse IDE, while pointing out what developers can expect to see improved. In Part I, we saw the creation of comments issue and explained the Javadoc skeleton. In Part II, we showed how managing existing documentation within the source code becomes easier and faster. We analyzed the kind of improvements custom Javadoc tags can generate, among which reducing occurrences of the NullPointerException is outstanding. Even though IDEs are already doing a great job at helping developers write quality code, there is room for improvements. Because the Eclipse platform is extensible, one can use plug-ins to implement those improvements. However, one should not dream too much. In the current state of technology, developers will still have to write the informal content in the comments for quite some time. It was our premise that good comments increase quality. However, code quality does not exclusively depend on static documentation; good design and tests contributes to code quality as well. Indeed, one should look on a large scale when evaluating tools. In an IDE, one should look at the supported aspects of one's methodology. With its extensibility, the Eclipse platform is especially well equipped for supporting custom methodologies within a coherent and seamless environment. B. Le Charlier and P. Flener. Specifications are necessarily informal, or: Some more myths of formal methods. Journal of Systems and Software, Special Issue on Formal Methods Technology Transfer 40(3):275-296, March 1998. http://user.it.uu.se/~pierref/publications.html Barbara Liskov and John Guttag. Program Development in Java: Abstraction, Specification, and OO Design. Addison-Wesley Pub Co 2000 ISBN: 0201657686 Barbara Liskov, "Data Abstraction and Hierarchy," SIGPLAN Notices, 23, 5 (May, 1988). Source code for this article: Source.zip Java Language Specification: http://java.sun.com/docs/books/jls/ Checkstyle: http://checkstyle.sourceforge.net/ CodeGuide: http://www.omnicore.com/index.htm Borland JBuilder: http://www.borland.com/jbuilder/ Eclipse: http://www.eclipse.com Net Beans: http://www.netbeans.org/ MS Visual Studio: http://msdn.microsoft.com/vstudio/ IntelliJ IDEA: http://www.jetbrains.com/idea/ Oracle JDeveloper: http://otn.oracle.com/products/jdev/index.html Charles-Philip Bentley is a computer science engineering student at the Catholic University of Louvain (UCL). He is currently writing his dissertation titled "Research on tools for code specification and commenting." His main interests are OOP methodologies, user interface ergonomics, and software engineering techniques. Baudouin Le Charlier is a professor at the Catholic University of Louvain (UCL). His research interest includes program analysis by abstract interpretation, program verification, intrusion detection, and interoperability of programming languages. He is teaching programming methodology, programming languages concepts, and formal syntax theory.
Can IDEs Do More to Improve Code Quality? Part II
April 28, 2004
Comment Maintenance
Code Convention
Formatting conventions
Naming conventions
private String _ownerName;
public static final int LIGHT_SPEED = ...;
Naming methods and variable identifiers
Key Concept: Names should be self-documenting.
Synchronization Between Code and Comments
//Comment explaining why D is used
int result = D*F+G;
Remark: Broadly speaking, when redundant information is present, synchronization becomes difficult if the system has to stay coherent.
/** The car's name is now name
*/
public void setName(String name) {
_carNameString = name;
}
/** The car's name is now {@var name}
*/
public void setName(String name) {
_carNameString = name;
}
Custom Javadoc Tags for Better Code
Pre-conditions and post-conditions
Invariants
Possible use of invariants: case study
/**
* Defines a Car
* @author Charles-Philip Bentley
*/
public class Car implements IWeighable {
/**
* Constant to use for null owner names
*/
public static final String NULL_OWNER = "NOBODY";
/**
* @def the name of the car owner
* @inv _ownerName!=null use {@link Car#NULL_OWNER} for
* creating a car without an owner
*/
public Car(String ownerName) {
_ownerName = ownerName;
}
/**
* @pre ownerName!=null use {@link Car#NULL_OWNER} for
* creating a car without an owner
* @param ownerName the name of the car owner
*/
public Car(String ownerName) {
_ownerName = ownerName;
}
public void setOwnerName(String newOwnerName) {
_ownerName = newOwnerName;
}
/**
* @pre newOwnerName!=null use {@link Car#NULL_OWNER} for
* creating a car without an owner
* @param newOwnerName the name of the car owner
* @post true the name of the car owner is set
* to {var newOwnerName}
*/
public void setOwnerName(String newOwnerName) {
_ownerName = newOwnerName;
}
/** Gives examples of uses for the Car class
* @author Charles-Philip Bentley
*/
public class ExampleCar {
public static void main(String[] args) {
Car car = new Car("Bob");
String name = null;
car.setOwnerName(name);
}
}
Key point: By having a well defined environment for documentation and comments, the IDE leverages the documentation and improves the developer's experience.
Synchronization Between Class Comments and Sub Class Comments
The case of the Liskov Substitution Principle
If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T.
/**Returns the weight of this object when empty
* @pre unit != null
* @param unit the unit to use for the weight
* @return w = the weight value of the object in the units defined by unit.
* @post w >= 0
* @throws WeightException when at least one of the contained objects
* does not implement IWeighable
*/
public int getWeight(IUnit unit) throws WeightException;
/**Returns the weight of this object when empty
* @pre unit != null
* @param unit the unit to use for the weight
* @return w = the weight value of the object in the units defined by unit.
* @post w >= 0
* @throws WeightException when at least one of the contained objects
* does not implement IWeighable
*/
public int getWeight(IUnit unit) throws WeightException {
//implementation
}
Re-synchronization upon API changes
Miscellaneous Uses and Features for Comments
Non-intrusive popup window
public static void main(String[] args) {
Car car = new Car("Bob");
String name = null;
car.setOwnerName(name);
}
Generating and Displaying the Javadoc
Javadoc comment Folding and Unfolding
Conclusion
References
Resources
About the Authors
cbentley@student.info.ucl.ac.be
blc@info.ucl.ac.be