The FLEX coding conventions are based on Sun's standard Code Conventions for the Java Programming Language, so you should probably familiarize yourself with that, first. See the javadoc style standards as well. We differ in a number of points, so read this document, too. The emacs JDE mode that we recommend on the Getting Started With Flex page adheres to these conventions, and the FLEX packages comes with a configuration file for JDE which makes it easy to generate code with the FLEX standard header, described below.
Package naming: We're much too lazy to spell out
edu.mit.lcs.flexc
in the package line of every single
class in our infrastructure, as Sun would recommend, so we root our packages at
harpooon
(eventually, flexc
). C. Scott has a personal
naming convention that capitalizes the names of all directories so
that they always appear sorted at the beginning of his
ls
, and the FLEX packages have inherited this naming
convention. Besides, BiCapitalized names seem to be easier to read
than alllowercasenames, and the_excessive_use_of_underscores strikes
us as somewhat ugly.
Beginning comments: Sun uses a c-style comment for the class file name, copyright
notice, and such; FLEX uses a more compact c++-style comment instead.
The first three lines of every FLEX source file should look like the
following (example taken from harpoon.Main.Main
):
The first line has the file name, and the creation time, date, and user. The second line claims copyright, and the third line gives it away. The second and third lines are very important because as use of the infrastructure increases we must be certain that all contributed code can be released under a uniform license. If permission is ever requested to release parts of the code base under some different license, we must know who the copyright holders are so that we can contact them.// Main.java, created Fri Aug 7 10:22:20 1998 by cananian // Copyright (C) 1998 C. Scott Ananian// Licensed under the terms of the GNU GPL; see COPYING for details.
The sed
script (man sed
!) named
bin/copyright.sed
in the FLEX distribution can be used in
conjunction with the munge
and unmunge
scripts to add copyright notices in bulk. A much easier way is to use
the emacs JDE mode to create your new class files, which (among other
things) does this for you.
Import statements: The use of * wildcards in
import
statements is discouraged. Not forbidden, but I'd
prefer you take the time to enumerate precisely what the class
dependencies of your code are. In addition, java compilers in general
(and jikes
in particular) tend to have a hard time with
the wildcards. Generally what happens is that the compiler, when it
sees the star, zips on over to the directory in the CLASSPATH that is
supposed to have the wildcarded classes, does an 'ls', and imports the
classes it finds. When you're building the project from scratch, it
finds no classes and imports nothing, causing many many compile errors
to confuse the poor user. javac
seems to be smarter
about this these days (which is why I recommend that make
java
be the first command you enter after a new installation or
a make clean
) but the flex makefile used to have huge
heinous carefully constructed 'bootstrap' sequences to get the code
working from scratch the first time all because of the liberal use
of import foo.*
statements. So avoid them if
possible, and make your java compiler happy.
Indentation: The Sun java coding standards set the four space indent firmly in stone. We merciful FLEX gods
are rather opposed to dictatorial whitespace
standards, and believe in freedom, free expression, and cute furry
bunnies (on alternate thursdays, at least). However, we
strongly recommend you stick with four-space indents, as it makes it
easier for others to edit your code without M-x set-variable
c-basic-offset
'ing every five minutes. For those few that will
insist that four spaces are much too many, and that your code doesn't
fit on the screen well that way, a careful study of the Linux kernel coding standards is in
order.
We will note here than 80 column lines are preferred, but we don't plan on reformatting all Darko's code.
Documentation comments: With in documentation comments, all class, variable, field, or method names should be enclosed in <code> tags. This (aside from aiding the reader) allows our automagic cross-reference tool to generate links to the appropriate class or method definition in the generate documentation. For this reason, you should be careful to enclose only the identifier---and not spaces or a final 's' to form a plural---in the <code>...</code> pair. Plurals should be written as:
so that the link is to a class named "HCodeElement" and not to a class named "HCodeElements". Note that the entire package+class string (the <code>HCodeElement<code>s are then...
harpoon.ClassFile.HCodeElement
) in a <code> block
also works and is recommended when there might be an ambiguity---for
instance between two classes named foo.bar
and
re.bar
. Trailing context works, too
(ClassFile.HCodeElement
) which may be more readable in some
instances.Class description block: The class description block should always contain the author's name, a valid email address, and the CVS version string, as follows:
Note that only the string/** *Main
is the command-line interface to the compiler. * * @author C. Scott Ananian* @version $Id: Main.java,v 1.8.2.6 1999/02/09 03:59:30 cananian Exp $ */
$Id$
needs to be included in the @version
tag; cvs will automatically expand and update the entry as you check in the file.
Case is important in keywords.
The emacs JDE mode will automatically create a class description block of the proper format. You just have to fill it in.
Requires/Modifies/Effects clauses: This is much more an issue of programmer taste, but this particular javadoc'ing style works well for Felix and seems to generate very clear and readable documentation. So, for your perusal, here are Felix's Notes On Documentation:
I try to document all of my methods with a set of three clauses, in addition to whatever explanatory notes on usage and related methods/classes I document. These clauses are:
- The REQUIRES clause
- The MODIFIES clause
- The EFFECTS clause
I try to put these clauses at the end of my method documentation and make them stand out from the rest of the javadoc using
<BR>
and<B>
tags.
- REQUIRES:
"The requires clause states the constraints under which the abstraction is defined. The requires clause is needed if the procedure is partial, that is, if its behavior is not defined for some inputs. If the procedure is total, that is, if its behavior is defined for all inputs, the requires clause can be omitted. In this case, the only restrictions on a legal call are those implied by the header, that is the number and types of the arguments." [Liskov pgs 44-45]
I often write my requires clause as a list of conditions, as follows:
Although if there is only one condition then I omit the list. [...] When I learn emacs-lisp, I'll write up macros to do all this stuff auto-magically. requires:
- condition one
- condition two ...
It is good practice to check the requirements at the start of the method and to use
harpoon.Util.Util.assert
to make the program stop if they do not hold. However, some cases it will be impossible to check a requirement (for example,/** Does crazy Turing stuff. probably isn't going to be checkable for all p). In any case, if the requires clause is violated, ANY possible effect is legal for the procedure, from an "intuitively correct" execution to an infinite loop. So try not to violate the requires clause for procedures you call.
requires:p
halts. */ void turing(Program p)- MODIFIES:
"The modifies clause lists the names of any inputs that are modified by the procedure; it can be omitted when no inputs are modified. The absence of the modifies clause means that none of the inputs are modified." [ibid pg 45]
For object methods (as opposed to static class methods), '
this
' (as in the specific instance of theObject
being operated on) is an implicit argument. However, it is not implicitly a member of the modifies clause, so any mutator method of anObject
should have in its modifies clause the following item: (at least)Don't list parts of the internal rep in the javadoc modifies clause, since we don't want to expose it. For really hairy code, I sometimes write two sets of clauses: the procedure as seen by the outside world in the javadoc, and another modifies/effects clause in a comment inside the method itself.
modifies:this
Parts of '
this
' that ARE exposed to the outside world (ie, public member instance variables) that are modified in the method should be listed separately from the 'this
' listed in the modifies clause. In general, you shouldn't need to worry about this because you shouldn't have variables that are exposed to the outside world, but in some cases we do have exposed variables and they need to be documented just like any other variable (they probably need to be documented more-so than other variables)- EFFECTS:
"Finally, the effects clause describes the behavior of the procedure for all inputs not ruled out by the requires clause. It must define what outputs are produced and also what modification are made to the inputs listed in the modifies clause. The effects clause is written under the assumption that the requires clause is satisfied, and it says nothing about the procedure's behavior when the requires clause is not satisfied." [ibid pg 45]
Effects clauses can be fairly difficult to write, because:
- It's hard to denote certain things (like Set Relations) in HTML. (Or have I been missing out on the HTML-Tags for Subset, Union, etc. all this time?)
- It's hard to expose the modifications that are made to inputs like '
this
' without exposing its internal rep (an abstract model of each object would help alleviate this, but those are really hard and time-consuming to write and even harder to write correctly.- It's hard to write an effects clause that manages to completely specify the operation of the procedure without essentially rewriting the procedure in pseudo-code in the body of the javadoc method (if this is your last resort, you may want to just forego writing a completely spec'd out effects clause, since someone who's going to try to make sense of your pseudo-code will probably also just look at the source of the method itself; although others in the group may not agree with this statement -- opinions guys? How nosy are we as coders?)
So try to get the effects clause right, but more importantly, WRITE IN THE DOC when you think you've got it wrong. That way people will know to "handle-with-care."
Make sure you include a High-Level description of what the method does somewhere in the javadoc (not necessarily in the effects clause), so that someone who's just browsing the javadoc trying to find the right tool for their job can easily filter out methods that aren't applicable to their problem.
PS. I've violated every one of these rules somewhere in my documentation, and the only reason is that I, like everyone other good LarryWallian coder, am lazy. But try to at least put a marker in the Doc saying FIX-ME, FILL-ME-IN, TO-DO, or something similar so that we can go back and fix all this stuff later.
Please comment your methods well. Every public class and
method should have an explanation in javadoc. Possible
exception is granted for those methods which are "intuitively
obvious"---toString
comes to mind as an example---but it can't hurt to comment even those. No exception can be made for
the toplevel description of public classes: if it's public it is meant
to be used by others and you have a responsibility to document that
use. Doubly so for interfaces; other people are not just using
your code but implementing your interface, and you need to give
them the information they need to do so. Any odd behaviors should be
rigorously documented, and be aware that things that seem "obvious" to
you (that your graph algorithm requires a representation with edges,
for example) are probably not so obvious as you may think.
Compounding the problem, java's type system does not allow us to
enforce certain constraints; these should be vigorously documented.
Write descriptions for new packages. If you add a new
package/directory to the codebase, you should also write a
packages.html
file describing what the package is for.
See Analysis/DataFlow/package.html
in the FLEX source.
The first sentence after the <body>
tag goes in the
package summary at top-level, and the entire contents of the html
body is put at the bottom of the package class summary page in the
javadoc. The results look like this.
Make your failures as spectacular as possible. One of
the Multics developers once
complained to Dennis
Ritchie (co-developer of UNIX and a former Multician) than he was
spending half his time writing error recovery code. Ritchie replied,
oh---we left all that stuff out of Unix. If something goes wrong we
just call a routine called panic
, the machine crashes, and you holler down the hall to reboot it
(Story
here).
The lesson here is that engineering reliable systems is easier if
failures are obvious. The goal is to write software with no bugs.
Generous use of the assert
function in harpoon.Util.Util
ensures that things are working the way
you expect them to. Similarly, if your code requires an input object
to implement some interface, a bare cast is usually sufficient. The
ClassCastException
thrown by the JVM is the proper way to
crash in this case. A related idea is that crashes, when they occur,
should make it obvious what went wrong. If your graph algorithm
requires the graph to be strongly connected (for example), I'd prefer
that your algorithm check this and crash early rather than silently
returning wrong results, or crashing in some obscure subroutine for
nonobvious reasons.
Implement toString()
. Most java debuggers use the
built-in toString
method to display the values of object
variables, so properly implementing toString
makes
debugging much more pleasant. Again, this goes doubly for
public
classes.
Use final
and private
.
Aggressively add final
tags to immutable fields in
objects; remember that using a public final
field for an
read-only object property is often more efficient than using a
private
field and a public
accessor method.
Further, aggressively hide information not relevant to users of your
class by using private
modifiers. Private
methods can be statically (not virtually) dispatched, which makes them
faster.
instanceof
is evil. Excessive use violates
the object-oriented design style, and hides the underlying
message-passing. It is preferred that you implement a superclass
method instead (o.isMOVE()
instead of o instanceof
MOVE
), or a Visitor
pattern where appropriate.
Use of instanceof
in the implementation of
equals()
methods should be done as follows, using
try-catch blocks:
Be wary of including too much in yourpublic boolean equals(Object o) { MyClass mc; try { mc = (MyClass) o; } catch (ClassCastException e) { return false; } return mc!=null && mc.is_really_equal_to(this); }
try
block; you
don't want to make ClassCastException
s thrown erroneously
by method involved in the is_really_equal_to
check.
I will tolerate reasonable use of instanceof
in
justified circumstances, but Martin probably won't.
Write good summary sentences. javadoc uses the contents of your method comment up to the first period as a summary sentence in the method summary at the top of the generated html, so try to make the first sentence stand on its own. See also Sun's comments on this, which include how to deal with period-terminated abbreviations in the summary sentence.
Use immutable collections for return values. This helps prevent nasty sneaky bugs caused by someone inadvertently modifying your collections behind your back. The Collections API has methods to make this easy, and we've also got some FLEX architecture to support this for array types. Reading what Sun has to say can't hurt, either.
Use the Java Collections Framework. We're gradually shifting the FLEX codebase from arrays, hashtables and vectors to the new collections framework; all new code should use the collections framework exclusively. Please do not use any of the following FLEX utility classes in new code; use the appropriate Collection instead:
harpoon.Util.Set
(use java.util.Set
instead)
harpoon.Util.HashSet
(use java.util.HashSet
instead)
harpoon.Util.TreeSet
(use java.util.TreeSet
instead)
harpoon.Util.ArrayEnumerator
,
harpoon.Util.CombineEnumerator
,
harpoon.Util.FilterEnumerator
,
harpoon.Util.NullEnumerator
,
harpoon.Util.ReverseEnumerator
,
harpoon.Util.SingletonEnumerator
(use the corresponding Iterator
classes instead)
harpoon.Util.FIFO
, harpoon.Util.UniqueFIFO
(use java.util.List
instead)
In the same vein, use java.util.HashMap
instead of
java.util.Hashtable
and java.util.ArrayList
instead of java.util.Vector
. Retrofitting these to existing
code isn't hard, either.
Verbatim copying and distribution is permitted in any medium, provided this notice is preserved. |
cananian@mit.edu HTML Last updated: 16 June 1999 |