Rightis more important than
fast
There are two ways of constructing a software design. One way is to make it so simple that there are obviously no deficiencies. And the other way is to make it so complicated that there are no obvious deficiencies.
C.A.R. Hoare
Program code is read by two audiences: people, and compilers. Both must be satisfied. Write your code so that there are obviously no deficiencies. Complicated code always has deficiencies, they're just harder to find and harder to fix.
Rightis more important than
fast
First get it right, then maybe get it efficient.
Only make it efficient if efficiency is needed for that part of the code. But always make it right. Correctness is always needed for every part of your code. It is often the case that early effort spent making code efficient gets in the way of making it right, and frequently is thrown away anyway when the specification changes.
It is never appropriate to make it fast before making it right. After all, it's easy to write software that does the wrong thing quickly (if you are in a real hurry, just use the class below).
public class FastButWrong {
public FastButWrong() { throw new RuntimeException(); }
}
ccfor the character (but there must be just one character that your program deals with at a time for this to be effective),
jand
kfor integer indexes (but there must be just one or two possible things being indexed), and
pv,
cu, and
nxfor the previous, current, and next one (but there must be just one kind of thing that your program deals with in that region of your code).
Formula left; Formula right; /** Constructs the disjunction of two subformulas. @param _left The first subformula. @param _right The second subformula. */ public Disjunction(Formula _left, Formula _right) { left = _left; right = _right; }
Someone should be able to use your packages, classes, and methods correctly after reading just the javadoc description.
If it is hard to come up with javadoc comments and variable/method/class/package names that clearly describe what your code is doing, then your code is probably either
Try writing a simpler, clearer description. Instead of struggling to write a description that matchs the code, write a clear description and make the code match that. Reorganize your design so that you can describe the components easily and simply.
These rules have a long history of helping people write better code.
ifladder with one or more
else ifs, put a /**/ comment after the first
ifkeyword so the conditions are lined up, and use whitespace to line up other things that go together (like the braces, or like the parts of the @param comments in the example further down).
if /**/ (null == result) { … } else if (0 == result.length()) { … } else { … }
// NO! if (onlyOne) m = 1; // this is bad
// Yes. Always use braces. if (onlyOne) { m = 1; }
=instead of
==, the compiler will flag a syntax error.
// NO!
if (ref == null) { … } // this is unwise
// Yes. Put the rvalue on the left. if (null == ref) { … }
An lvalue (pronounced ell-value
)
is an expression that can be on the left side
of an assignment —
a variable, or an expression that produces a variable.
An rvalue (pronounced are-value
)
is an expression that can only be on the right side
of an assignment —
a value, or an expression that produces a value.)
// The compiler will catch that the method name is misspelled:
@Override
public String toStrink() { … }
Where possible, make objects immutable. That is, design your classes so that the value of each object of a class is set when the object is constructed, and never changed. Taking two examples from java.lang: String objects are immutable, and StringBuffer objects are mutable. Strings are much easier to reason about and get right.
Java provides the final keyword, whose effect is related but different: if you declare a variable, parameter, or attribute final then it cannot be changed to refer to a different value or object. That is quite useful! But note that for example a final StringBuffer variable is still mutable; the final just means that no other StringBuffer can be assigned to the variable.
Researchers have long known that using immutable objects makes it far, far simpler to construct a proof of correctness for a program or an aspect of a program. More recently, the best practitioners have realized that it also makes it far, far simpler to understand and reason about your programs. That means they are easier to design, easier to write, and easier to debug. If an object is immutable, then you don't have to take its context and history into consideration, and context and history are the aspects that are the most difficult to understand and reason about.
As you write your code, put in assertion checks for every condition you are assuming is true:
if (!conditionYouAssume) { throw new AppropriateRuntimeException("Informative message"); }
(Don't include such exceptions in the throws clause of the method, as they are not there to be caught but to kill the process and print a stack trace.)
If you have access to my exception package, you will find there a wide selection of kinds of exceptions to choose from. If you don't have access to an appropriate exception, write a new exception class to use (it's easy and fun).
A good rule of thumb is: if you realize you are assuming something while you are writing code, stop and put in assertion code that checks that the assumption holds at the point where your code assumes it and throws an informative exception if the assumption is violated. Assertion checks are always appropriate if:
At a minimum, write checks of incoming parameter and attribute values for every method. You should include the exceptions for parameter checks in @throws tags for the method's javadoc, with an informative description such as.
@throws NullPointerException If _parameter is null.
Assertion checking is crucial for software whose external and internal interfaces are unstable, as is almost always the case in research projects.
Testing is essential, but it's difficult to test code effectively if you don't know what it is supposed to do yet.
If you are in a prototyping situation, as is often the case in writing code for research projects, you should not spend too much time on writing tests (write assertions instead). Just write tests for what you are pretty sure your code is going to have to do.
As the specification for your code stabilizes, add more tests. If your code is something whose correctness is going to be essential, then write a full set of tests when the specification settles.
Run your tests often, even early on when you don't have many.
if (…) { … } while (…) { … }
if (…) { while (…) { … } }
if (…) { x = 2; } ^^ ^^ while (…) { y += 5; }
/** Align the start and end markers with each other and the entity you are commenting. */ public interface Javadoc { /** Short ones can go on a single line (two spaces before and after). */ String oneLiners(); /** Longer ones have the start marker on a line by itself, and the end marker on a line by itself. Text is indented two spaces further in. */ String longerOnes(); }
The Eclipse placement of a * at the beginning of each line is troublesome to do in a text editor and not worth the effort.
getmethod that returns one of the class's attributes, use the same name for both.
/** Returns the left subformula. */ public Formula left() { return left; }
setmethod that sets one of the class's attributes, use the same names for everything. Prefix the parameter name with an underscore to distinguish it.
/** Sets the left subformula. */ public void left(Formula _left) { left = _left; }
getor
setin your method names. Instead, fix the conceptual model you are using for the class, or the names you have given to the concepts. If it seems necessary to use
getor
setin order to distinguish the methods when searching the source code, then change to a capable text editor or development environment, or learn to use regular expression searching in your current editor.