Computer Science 15-111 (Sections A & B), Spring 2007 Style Guide |
This is our
official Style Guide. Most of these
guidelines are industry standards. In
some cases, there are several reasonable alternatives, but for the purposes of
this course, we will standardize on one
of them. This is common in industry,
where software development teams have their own specific coding standards which
each developer must abide by. Also, to
be more suitable for a 100-level course, some rules have been simplified and others
have been dropped entirely. Finally,
some rules are not actually style rules but correctness rules (such as that
your code must compile and must run), and are included here to make this list
more suitable as a rubric for grading your submitted programs.
When these
rules are exact, they are to be applied exactly. So, for example, when a rule states that
there shall be one space between an “if” and the subsequent parenthesis, then
there must be exactly one space –
anything else is a style error.
These are
followed by an Anti-Style Guide, a list of some style rules which are not
uncommon in other sources, and which you should be aware of, though we will not
use them in our code.
Note that
sample code, especially when developed in class under tight time constraints
and for specific purposes, will violate some guidelines. Except when noted, if you use sample code in
your own programs, you must bring that code into conformance with these
guidelines.
Summary:
1. Your code must compile |
30. Label null statements |
59. Do not use negated if’s |
2. Your code must run |
31. Label fall-through
cases |
60. Avoid boolean
side-effects |
3. Use a clear, robust design |
32. Declare array types |
61. Clear obsolete
references |
4. Write top-down code |
33. Use concise names |
62. Do not use == for
floats |
5. Write concise methods |
34. Use precise names |
63. Do not use == for
equals() |
6. Do not teach Java 101 |
35. Do not abbreviate names |
64. Use Arrays.equals() |
7. Use clear comments |
36. Do not misspell names |
65. Override hashCode() |
8. Use concise comments |
37. Avoid single-letter
names |
66. Avoid calling paint() |
9. Use accurate comments |
38. Use standard names |
67. Avoid paint() side
effects |
10. Use no extra comments |
39. Use suitable names: |
68. Make variables private |
11. Do not misspell
comments |
40. class names |
69. Do not use default
visibility |
12. Use file comments |
41. variable names |
70. Properly declare
constants |
13. Use class comments |
42. method names |
71. Use accessors &
mutators |
14. Use method comments |
43. parameter names |
72. Be efficient |
15. Use in-line comments |
44. constant names |
73. Avoid creating new
objects |
16. Do not use javadoc |
45. boolean names |
74. Use StringBuilder |
17. Use no block comments |
46. Qualify members with
"this" |
75. Use appropriate initial
sizes |
18. Do not exceed margins |
47. Qualify statics with
"Class." |
76. Avoid recursion |
19. Indent wrapped lines |
48. Do not duplicate code |
77. Use splash screens |
20. Format blocks properly |
49. Do not use useless code |
78. Use clear, precise
prompts |
21. Use whitespace |
50. Do not use Magic
Numbers |
79. Robustly process user
input |
22. Do not overuse
whitespace |
51. Limit variable scope |
80. Do not expose
exceptions |
23. Format comments
properly |
52. Use helper variables |
81. Use clear output |
24. Use spaces not tabs |
53. Handle errors |
82. Use formatted output |
25. Use proper spacing: |
54. Use assertions |
83. Use a tasteful,
polished GUI |
26. for if/for/while/.. |
55. Use test code |
84. Avoid useless “glitz” |
27. for methods |
56. Use enums |
85. Be polite |
28. for expressions |
57. Use else |
|
29. for
semicolons |
58. Use parentheses
liberally |
|
General Guidelines |
|
Your code
must compile |
Your code
must compile with no compiler errors.
If you must, comment out code as necessary so that what you submit
compiles without errors. |
Your code
must run |
Your code
must run. Whether it runs correctly is a separate matter. This rule merely states that it is runnable according to the problem
specification (often by invoking a main
method), and that it makes some attempt
to actually solve the given problem. |
Use a
clear, robust design |
Your
design must be clear, straightforward, and plainly evident upon inspection of
your code. It must also be robust, so
that it performs well when used or repurposed in unanticipated ways. |
Write
top-down code |
Your use
of “top-down programming” must be readily apparent in your code. Each step and sub-step of a solution or
algorithm should clearly correspond to methods. |
Write
concise methods |
Methods
must be concise. For our purposes,
methods may not in any case include more than 15 lines of actual code
(discounting comments and whitespace).
This is indeed an artifice, but it is probably correct 95% of the
time, and it is an easily-enforceable rule that helps you write concise,
structured methods. Note that, for
clear cases of run-on methods, this rule applies regardless of the “15-line”
limit. |
Documentation Guidelines |
|
Do not
teach Java 101 |
Assume
that your comments are being read by someone as knowledgeable as yourself. Do not provide comments that are obvious,
or that basically teach Java 101. For
example: String[] names; // names is an
array of Strings ß Wrong! |
Use clear
comments |
Comments
should use very clear, plain English.
Wordy and confusing comments can be worse than no comments at all! |
Use
concise comments |
When
comments are needed, keep them terse and to-the-point. Header comments may use full sentences, but
in-line comments often should not. For
example, if an array of student names is kept in ascending order, this
requires a comment (since it not
obvious just from the data structure).
Here is the wrong way to do this:
// and it is in ascending order String[] studentNames; And a
better way: |
Use accurate
comments |
Be sure
your comments and your code actually correspond to each other! Errors in your comments are very confusing. |
Use no extra
comments |
Only
comment where necessary, including file, class, and method header comments,
and then only where there is potentially confusing, complex, interesting, or
otherwise noteworthy code. Extra
comments provide no additional information, and merely distract from your
code. |
Do not
misspell comments |
You might
think this is a small matter, but it is not.
At the least, misspellings in your comments make your code seem
amateurish. At worst, it can lead to
misunderstandings. |
Use file
comments |
Every
file must begin with a file header comment, including the file name, your
name, your CA’s name, the assignment and problem number, and a description of
the contents of the file, including the classes in the file and their API’s. |
Use class
comments |
Every
class must begin with a class header comment, including the class name and a
brief description of how the class fits in to the overall design of your
program. More details are in the file
header. |
Use method
comments |
Every
method must being with a method header comment, including the method name, a brief
description of its parameters and its return value, and then (except in
obvious cases) a brief description of how the method works. |
Use in-line
comments |
Unlike
header comments, which are mandatory for all files, classes and methods,
in-line comments are optional. These
are the comments that should be added to explain potentially confusing or
otherwise interesting code. Anywhere
that readers of your code might be confused, add an in-line comment to
tersely provide some more clarity to your code. |
Do not
use javadoc |
Javadoc
is a powerful documentation tool that comes with the Java SDK. Even though it is powerful and commonplace,
we will not use javadoc in this course.
Do not use javadoc in your code. |
Use no
block comments |
Except in
your file header comment, do not use block comments in your code. These are of the form /* */. The main problem is that block comments are
useful for grading purposes, so we can enable/disable portions of your code,
especially erroneous or non-compiling portions!, but block comments do not
nest, so your using them complicates our grading. |
Formatting Guidelines |
|
Do not
exceed margins |
No lines
of code can exceed 80 characters. No
exceptions. This allows your code to
display nicely even on smaller screens (or in larger fonts, as may be needed
by your vision-impaired colleagues), and also allows your code to print
nicely. |
Indent
wrapped lines |
When a
line of code would exceed 80
characters, you need to wrap it to the next line. The wrapping should be done at an operator or
a comma or other punctuation, and the next line should be indented so that it
conforms to the logical indentation of the previous line. For example: int x = foo(someMethodCall(), Notice
the second line is indented just inside the parenthesis. Here is another example: boolean ok = ((x > 0) && (x
< width) && (y > 0) && (y
< height)); |
Format blocks
properly |
Opening
left braces must be placed at the end of the line of code where the block
commences. The statements in the block
must be indented once (3 spaces) beyond the commencing line. Closing right braces must be placed on a
line by themselves and indented at the same level as the commencing
line. As in: while (someTest()) { // ß opening brace doSomething(); // ß indented body doSomethingElse(); } //
ß closing
brace |
Use whitespace |
Whitespace
should be used judiciously to make your code more readable. First, use single blank lines to separate
logical blocks of code. For example, member
variables should be grouped according to their purpose, with blank lines
separating blocks of member variables that are all related. When you do this, you often lead each such
block with a short comment. int x = left + (getWidth() – rectWidth )/2; int y = top + (getHeight() – rectHeight)/2; In this
example, each line has some extra whitespace added so that they align nicely. |
Do not
overuse whitespace |
Whitespace,
as with comments, should be used sparingly.
Separating logical blocks (the most common use of whitespace) can be
achieved with a single blank line.
Thus, in particular, you should never have two consecutive blank
lines. Also, do not include extra
spaces at the ends of lines or extra blank lines at the ends of files, as
these confound editing. And never have
a blank line precede a closing brace. Less
is more. |
Format comments
properly |
Header
comments left-align with the code they are describing. In-line comments occur on the same line as
the code only when (a) the comment applies to just that line; and (b) it fits
neatly on the line. Otherwise, in-line
comments appear on a separate line just above the code, indented to the same
level. |
Use spaces
not tabs |
Set your
IDE to indent using spaces rather than tabs, and then use 3 or 4 spaces per
indent level. This will allow your
code to print out more predictably, particularly when printing or when
editing in a different IDE with different tab settings. |
Use
proper spacing: |
Follow
each case below precisely: |
for if/for/while/.. |
Use this
format (where the dot · is exactly one space – do not
omit it, and do not use more than one space): if·(test) |
for methods |
Use this
format to call a method: foo·(x,y,z); // ß Wrong! However,
you may optionally include a single space after each comma between arguments,
but not before the closing parenthesis, as in: |
for expressions |
Generally,
place one space on either side of binary operators and nowhere else, as in: |
for semicolons |
Except
for null statements (see next item), never place a space (or other whitespace) before a
semicolon, and never place two semicolons consecutively. |
Label
null statements |
Null
statements are expressed in Java as a standalone semicolon. While these are legal, much more often than
not a standalone semicolon marks a mistake in your code where you
accidentally inserted an extra semicolon.
Unfortunately, being syntactically legal, the code still compiles,
only it does not work at all as anticipated.
For this reason, if you do intend to use a null statement, you must
indent the statement on its own line (always), and must add a clear comment
indicating that it is intentionally a null statement, as in: ; // do nothing (keep reading...) |
Label fall-through
cases |
This is
similar to the null statement problem.
In Java, it is legal to have a case in a switch
statement not end in a break, and so execution simply flows on
into the next case. While this is
occasionally desirable, much more often it marks a mistake in your code where
you simply forgot to include the break. For this reason,
if you mean for a case to fall-through, you must comment it as such, as in: case APRIL:
fileAnnualTaxes(); // fall-through case JANUARY: case JULY: break; } |
Declare
array types |
There are
two ways to declare arrays in Java: int x[], y; // x is an array, but y is not! The first
case declares two int arrays, x and y.
The second case, however, declares an int array x and just an int (not
an array!) y. This is potentially
confusing, since with all other type declarations, all variables declared at
once are of the same type. Thus, for
consistency, we use the first case only.
That is, declare your arrays this way: And not
this way: int x[], y[]; // ß Wrong! Finally,
be sure not to place an extra space after the type and before the brackets,
as in: int·[] x, y; // ß Wrong (extra space)! |
Naming Guidelines |
|
Use concise
names |
Names
must be concise. So, studentsInCsClasses is ok, but studentsTakingComputerScienceClasses is not. |
Use precise
names |
Names,
while concise, must precisely describe methods, variables, and classes. So, getValue is a poor name for a method that gets and increments the value. getAndIncrementValue is better.
Often, an exceedingly-long name indicates that a method is being
stretched beyond a single purpose, and should be split into multiple methods. |
Do not
abbreviate names |
Do not
abbreviate names. So, instead of curval, use currentValue.
If a name gets to be too long, think of a clever way to rename it to
keep the semantics in a more concise name. |
Do not
misspell names |
This is
like the misspelled comments problem noted above, only worse, as misspelling
names may even force other programmers to reluctantly adopt your
misspellings! Not good! |
Avoid single-letter
names |
Single-letter
names (x, y, i, j) may be used only in two cases: first, when there is little meaning to a
variable (as in the problem: “read a
number and print whether it is prime”; the number you read in can be called x, it need not be called number); and second, when it is a
standard name, such as an (x,y) location in graphics, or an (i,j) location in
a matrix. |
Use
standard names |
Use
standard names for standard functions.
Board games, for example, use “row” and “col” for their dimensions,
and graphics use “x” and “y” or “left” and “top” (often your choice). Note that “col” is an abbreviation, but
because it is (somewhat) a standard, it is acceptable. |
Use
suitable names: |
Follow
each of the cases below precisely: |
class names |
Class
names should be simple and descriptive nouns, and should be in MixedCase (the
first letter of each word capitalized), with the first letter in
uppercase. While technically legal,
class names should not contain dollar signs ($) or underscores (_) – the
former are used by the compiler for inner classes, and the latter are just a
bad idea. |
variable names |
With the
exception of booleans (see below), variable names should also be nouns, and
in mixedCase with the first letter in lowercase. If the variable is a collection (array,
ArrayList, etc), the name should be plural, otherwise it should be singular. |
method names |
With the
exception of booleans (see below), method names should be verbs, and in mixedCase with the first
letter in lowercase. Except for
constructors, methods should never be given the same name as the enclosing
class. |
parameter names |
As
parameters are local variables, they are generally named in the same manner
as other variables, with one additional proviso: parameters to constructors, accessors, and
mutators that relate to specific member variables should take the same name
as those member variables, as in: private String title; // member variable public void setTitle(String title) { this.title = title; } |
constant names |
Constants
(such as Math.PI) should be declared as static and final
(and typically public), and should be named in ALL_CAPS
with words separated by underscores (_). |
boolean names |
Boolean
variables and methods should be phrased as yes-no questions, usually the form isProperty, as in isVisible, but occasionally in other forms, as in hasCousins. |
Qualify members
with "this" |
While not required by the compiler, we will qualify all member variables with “this.” Plus, if ever there might be any confusion whatsoever, qualify member methods with “this.” This helps novice programmers become more comfortable with member methods being invoked on the implicit object “this”, and avoids the confusion of local variables inadvertently shadowing instance variables. |
Qualify statics
with "Class." |
Similarly,
while not required by the compiler, we will qualify all static variables and methods with the class name, as in Math.PI.
As class names begin in uppercase, and variable names do not, this
provides an easy way to immediately discern when a variable or method is
static. |
Programming Guidelines: General |
|
Do not
duplicate code |
You
should never have the same, or the very-nearly same, code repeated in your
program, as doing so not only increases your coding time, but it greatly
increases the likelihood of errors and inconsistencies developing in your
code. This applies not just to methods
or statements, but even to all but the simplest of expressions. Generally, the way around this is to place
the common code in a method. If you
are dealing with very similar but not perfectly identical code, then you
should add parameters that allow your code to deal with the different cases
in a single method. Finally,
constructors should share common code either by calling a common initialize()
method or, more commonly, by directly calling each other, as in: public Foo() { this(10); // ß call other constructor } public Foo(int x) { } } |
Do not
use useless code |
There are
various constructs that are technically legal, but which have no logical
effect. These are not as benign as
they may seem, and often reflect an error in the program. As such, they should be excluded from your
code. For example, you should not
include a return as the last line of a void method: public void foo() { doSomething(); return;
// ß Wrong! } Nor
should you have a self-assignment with no effect: x = x; // ß Wrong! Nor
should you have unnecessary casts: String s = (String)"ugh"; // ß Wrong! |
Do not
use Magic Numbers |
With few
exceptions (usually, -2, -1, 0, 1, and 2), you should not use constant
literals directly in your code. These
are called “magic numbers”, since their value is assigned as if by
magic. For example, here is bad code: int x = 20; int width = 60; Where did
that 20 come from? What about the 60? Consider this modified version: int x = this.getWidth() / 5; int width = this.getWidth() – 2*x; Here, the
logic is evident – the values are no longer “magic”. What’s more, if the size of the window
changes, the code using magic numbers will be incorrect (still using the
fixed values), whereas the improved code will still work correctly. Note that
the exceptions of (-2, -1, 0, 1, and 2) are rules of thumb, and that
sometimes even these values are magic numbers. So, if you are iterating over the odd
numbers between 1 and n, you can do: for (int x=1; x<=n; x += 2) // ß no magic! But if
you use 2 in a situation where you could just as well have used 3, 4, or some
other magic value, then that 2 is also magic and must be placed in a variable
for clarity and robustness. |
Limit variable
scope |
Use the
narrowest scope that you can for your variables. So you should use local variables whenever
possible. Only use member variables
for values that must be retained across method calls. And only use static (class) variables for
values that are in fact always the same for every member of a class. The
exception to this rule: you should generally
widen the scope of immutable objects (like Strings and Integers) as there is rarely
any value in having local copies of these objects. |
Use helper
variables |
In
complex computations, it is often best to introduce “helper variables”. These hold intermediate results, allowing
you to break up confusing expressions into manageable pieces, and then to
label parts of those expressions so they make more sense. This makes your code more self-documenting
(a good thing). |
Handle errors |
In
general, you should anticipate the kinds of errors that may occur in your
code, and explicitly handle those conditions so that the errors do not crash
your code or otherwise place it in an undesirable or unexpected state. Note that we will not fully cover Java
exceptions, so you have some liberty in how
you handle errors. But you must handle
them in some way. |
Use assertions |
Your code
should self-document expected conditions in methods using “assert” statements, as in: public int findMin(int[] array) { assert(array != null); ... } An assert statement takes a single boolean
value, and if that value is false, throws an exception. Actually, it only does this if the –enableassertion (or –ea) flag is passed to the JVM, but
even if you do not use assertions at runtime, you should use them in the
compiler for self-documenting purposes. You
should use assertions to self-document three situations: preconditions
(these must be true when a method or block of code is invoked); postconditions (these must be true
after a method or block of code finishes executing); and invariants (these must always be true at the point where they are
declared, say within a loop). Note that
we will not require that you use every possible assertion that you
could. This would be oppressive, and
as unhelpful as commenting every line of code. However, any unexpected or especially
important conditions must be flagged with assertions. |
Use test
code |
Even if
it is not explicitly assigned, each program you write must include code that
tests that program. We will not
necessarily use Junit (a common testing framework), so you can write these
tests however you wish. But you must
somehow test the various methods you write, as well as the overall operation
of your program. A simple way to test
your code is with a series of assertions, as in: public void runTests() { // test the isPrime method: assert(isPrime(3)); assert(isPrime(4) == false); } The point
of testing, of course, is to detect errors.
For this reason, you should think about so-called boundary cases, where your code may use different logic than the
typical case. For example, it seems
like a good idea to test isPrime for 2 (the only even prime), 1 (a
commonly-mistaken non-prime), 0 (often mishandled), and a negative number
(say, -5). Finally, look up a large
prime online to see how it performs with large values. So we get: public void runTests() { // test the isPrime method: assert(isPrime(-5) == false); assert(isPrime(0) == false); assert(isPrime(1) == false); assert(isPrime(2)); assert(isPrime(3)); assert(isPrime(4) == false); assert(isPrime(104729)); assert(isPrime(104719) == false); } |
Use enums |
As
discussed in class, use enums when appropriate. For example, months (JANUARY,
FEBRUARY, etc), weekdays (MONDAY, TUESDAY, etc), and so on. |
Use else |
You
should never use successive “if” statements with logically exclusive
conditions. Instead, you must use
“else” statements. For example,
instead of: // Wrong! if (x < 0) doThis(); if (x == 0) doThat(); if (x > 0) doSomethingElse(); You
should use: // Right! if (x < 0) doThis(); else if (x == 0) doThat(); else doSomethingElse(); |
Use parentheses
liberally |
In
general, you should not rely on your knowledge of precedence and
associativity in complex expressions.
Instead, you should parenthesize every sub-expression. This leads to clearer code in most
circumstances, and very often avoids subtle bugs. |
Do not
use negated if’s |
As part
of the general rule of using the clearest, simplest code possible, you should
not use negated conditions in an “if-else” statement. So, instead of: if (!isPrime(x)) handleComposite(x); else handlePrime(x); You
should use: // Right! if (isPrime(x)) handlePrime(x); else handleComposite(x); |
Avoid
boolean side-effects |
Consider
the following code: boolean b = hasSomeProperty(); if (b = false) loadProperty(); This code
looks ok, but it has a nasty bug: the
test whether b equals false has only one equals (=), and so
instead it assigns false to b (oh no!), then (worse!) it
uses this boolean value as the result of the test. So this compiles, but the “if” condition will always be false, regardless of the result of hasSomeProperty(). While
there may be a time when this would be desirable behavior, it is generally so
undesirable that you should never use a boolean assignment inside a boolean
test. Split them into two separate
lines. |
Clear obsolete
references |
When a
non-local variable holding an object reference is permanently done with that
reference, you should explicitly set that variable to null.
This allows the runtime to finalize the object, releasing its
resources, and then to garbage collect it.
Local variable references will be handled automatically as the
variable falls out of scope. |
Do not
use == for floats |
As
discussed in class, == generally fails for comparisons of floating point
numbers, due to their approximate values.
Instead, you should write a method (called something like almostEqual)
that returns true if the two numbers are within some very small epsilon of
each other. So, instead of: float
f = foo(); if (f
== 2.5) doThis(); // ß Wrong! You
should use: if (almostEqual(f,2.5))
doThis(); // ß Right! |
Do not
use == for equals() |
Except for
arrays (see below) and in rare cases which must be well-documented, you
should not use == to compare objects.
Instead, use equals. This is
especially true for Strings. So,
instead of: You
should use: |
Use
Arrays.equals() |
Arrays
are objects, but they should not be compared using == nor using the object’s
equals method, as those each compare the actual reference, which is rarely
what is intended. Instead, if you wish
to compare the contents of the
arrays, you should use the Arrays.equals method. |
Override
hashCode() |
When you
override equals(), you must also override hashCode(), or your code will
suffer from subtle and confusing bugs.
See Sun’s documentation for Object.hashCode()
for details. |
Avoid
calling paint() |
In your
custom Swing component, you should override
paintComponent(), but you should never call this
method or any other paint() method directly! Swing calls these methods for you. If you need to let Swing know that your
application needs to be repainted, call repaint(). Soon after a call
to repaint(), Swing will call your paintComponent() method. |
Avoid paint() side effects |
In your
custom Swing component, you should override
paintComponent(), but be sure that your paint
methods do not have any side effects. They
should not modify any instance or static variables, but only access
these variables in order to paint the current state. Remember:
Swing calls paint methods, not you, and you do not control how often
Swing will do so. |
Programming Guidelines: Visibility Modifiers |
|
Make
variables private |
Make all
member variables private.
This is a simplification, and when you become more proficient, you may
at times use other visibility modifiers for member variables. But for this class, we will keep all member
variables private. |
Do not
use default visibility |
Do not
use “default visibility”, which is what you get when you do not include any
visibility modifier (and which provides visibility between public and private, similar to protected). |
Properly
declare constants |
True
constants (such as Math.PI) must be final and static,
and in nearly all cases they should also be public. |
Use
accessors & mutators |
Expose
member variables via accessors
named getProperty() and mutators
named setProperty(), where “Property” is the variable name. Here is standard usage: private String title; // ß private member public String getTitle() { return this.title; } public void setTitle(String title) { this.title = title; } |
Programming Guidelines: Efficiency |
|
Be
efficient |
In
general, your code should be efficient, which means to use as few resources
as practicable, especially time and space (memory and disk). You are
not expected in this course to always use the most efficient algorithm, but
you are expected to be at least modestly clever in considering efficiency. For
example, when testing a number for primality, you should explicitly check if
it is odd, then you should test only the odd numbers and then only from 3 to
the square root of the number. There
are even faster approaches, but this is good enough for our purposes. |
Avoid
creating new objects |
Creating
new objects is surprisingly expensive, and the wanton creation of new objects
can make code run very slowly and consume large amounts of memory (until it
is garbage collected, at least). For
example, instead of creating a new Color object with the same values on every
call to paintComponent, store this object in a member variable and reuse it
across calls. |
Use
StringBuilder |
A special
case of creating new objects is when constructing a String using
concatenation. As Strings are
immutable, this is a very expensive operation. Instead, use a StringBuilder, and then once
built obtain the String with the toString() method. |
Use
appropriate initial sizes |
When
using variable-length collections on large data sets, your performance will
go up considerably if you use good guesses at initial sizes (at least, when
you have these in hand). For example,
if you know you are about to load an ArrayList with about 100,000 elements,
you can allocate the list with, say, 150,000 elements, as: ArrayList myList = new ArrayList(150000); Of
course, as soon as you no longer need this list, you should set it equal to null to allow the garbage collector to
reclaim it. Note that
this approach is not useful if you do not have any basis for an initial
guess, and it is not necessary if you are not expecting a large amount of
data. |
Avoid
recursion |
Recursion
is generally slower than iteration, and so should be avoided in most
cases. The exceptions are when the
performance is similar and the clarity of recursion far exceeds that of
iteration. This is the case in
naturally recursive algorithms, such as tree traversals. |
User Interface Guidelines |
|
Use
splash screens |
Splash
screens are what you usually first see when a program is launched, and,
unless otherwise indicated, you should provide one (in the GUI for graphical
programs, and in the Console for text-based programs). State what the program does and how the
user operates it. Do not get overly
verbose – if you need to provide detailed instructions, you should provide a
“Help” command to access them. In any
case, this is your first chance to interact with the user, so provide a nice
experience here! |
Use
clear, precise prompts |
Users should always know what is happening with your program and why. Thus, you should always prompt the user for input, explaining in very clear, plain, and precise language what the program is expecting as input. For example, you should distinguish between the cases of “non-negative” and “positive”. If you feel your audience may not understand “non-negative”, then rephrase it, say to something like “a number that is 0 or greater”. Also, be sure to explain to users why they are providing input (for smaller programs, this may be done once in the splash screen). |
Robustly
process user input |
You
cannot demand that users have your expertise either in programming or in
math, not to mention the fact that users (like programmers) make
mistakes. Thus, you must handle
illegal user input robustly. For
example, if the user is prompted for a non-negative number, yet provides -5
as input, you should politely inform the user that this is not a non-negative
number and then ask again for input.
In any case, you may never crash, throw an exception, or place your
program in an unexpected or undesirable state in response to user input. |
Do not
expose exceptions |
Programmers
use exceptions to help detect and handle error conditions. This is a great tool for programmers, but
users should be entirely unaware of this mechanism. Often, error states can be handled without
any user notification. When users must
be notified, this should be done through a simple, clear, intuitive
dialog. Never let the user see an
actual Java exception! |
Use clear
output |
All
output from your program must be clear, concise, and informative. Do not ramble or use flowery language. |
Use
formatted output |
Always
format your output to help users.
Present floating point numbers with an appropriate number of decimal
places (for example, do not tell students that the average score on a quiz
was 83.2313598%). When presenting
tabular data, actually align it into a table!
In general, any time you place information in front of users, think
about what information you are
presenting as well as how you are
presenting it. |
Use a
tasteful, polished GUI |
Given how
the brain processes visual information, it is very easy to distract users
with garish colors, poor layouts (say, where items are centered improperly),
and such. This prevents your program
from being used effectively. Even if
users can use such a program, it feels amateurish. Thus, your graphical programs should always
be tastefully designed with a polished, error-free implementation. You don’t want scratches in the paint, even
if it doesn’t really affect the car’s performance, right? |
Avoid
useless “glitz” |
Resist
the urge to add unnecessary features to your user interface. Everything either adds or detracts, so if
it does not add, it necessarily detracts.
For example, if you are presenting a page of text explaining how to
operate your program, do not randomly assign different colors to different
lines of text. The problem is that
users will not assume the colors are random, and will invent their own
mythology about them. Thus, these
seemingly innocuous color changes have made your users less able to
understand your program! Of course,
you can use color changes as a great tool to help organize information, but
then that is not random and so this rule does not apply. |
Be polite |
Always
show respect for users. Do not
condescend. Do not be unkind. Always be polite. |
Anti-Style Guide |
This Anti-Style Guide is a list of some style
rules which are not uncommon in other sources, and which you should be aware
of, though we will not use them in our code.
This is not to say that they are incorrect (they are not, there is some
choice in these matters of style) or not important (in fact, in some cases,
such as with accessibility, they are both morally and legally essential to
address in commercial products!). However,
you will not be required in general to address them in this 100-level course
(again, with accessibility, there are numerous tools and techniques available
to help programmers in this regard, and these are covered in later courses).
Anti-Rule
1: Always use braces with if/for/while/do [ you may omit the braces ]
This is a
common rule, though not enforced by the Java compiler. It disallows this code:
while (x < 3)
doThis();
Why? The problem is when you later realize that you
had to add another statement to the body of the while loop, so you edit the
code as such:
while (x < 3)
doThis();
doThat(); // ß Wrong!
Confusing indenting!
Of course,
the indenting here has no meaning to the compiler (it is just for us humans),
so this code is in fact the same as:
while (x < 3)
doThis();
doThat();
We can
plainly see that doThat is not called in the loop. To avoid this error, you can adopt the policy
of always using braces, but we will not require
that you do so.
Anti-Rule
2: Never put an entire if or while statement on one line [ it is ok in some cases ]
This rule
disallows this code:
while (x < 10) if ((y > 2) &&
foo(z)) for(int i=1; i<foo(x); i++) ;
And that is
a good thing – placing all that on one line is utterly confusing! However, the rule also disallows this code:
while (x < 10) foo(x++);
In this
case, with such a simple loop condition and such a simple loop body, it all
fits very neatly and clearly on one line.
In such cases, you may in fact place the code on one line.
Anti-Rule
3: Do not use continue or break in loops [ you may do this ]
The
brilliant computer scientist Edsger Dijkstra advocated an approach termed
“Structured Programming”. You may read
about this on Wikipedia if you like! One
aspect of this approach is that it forbids the use of continue or break in loops. The
terminating logic of the loop must be contained in the loop’s conditional test,
and continuing logic must be replaced with if-else structures. There are many supporters of this approach,
and it most assuredly has some nice theoretical properties. However, practically
speaking, it can lead to stilted code, and it is often true that a continue or a break statement can make a loop’s logic much easier to express
and understand. Thus, you may use these
statements, but you should be careful to use them to make your code clearer.
Anti-Rule
4: Do not use return statements except at the
end of a method [ you may do this ]
This is
another example of Structured Programming, and we will not abide by it. Note, though, that you must make all return statements very clear to the
reader!
Anti-Rule
5: Always use default in a switch statement [ you may omit the default case ]
The logic
behind this anti-rule is that cases may be added later which invalidate your
default logic, and so in theory by having a default case your code may be a bit more robust. This may be true, or maybe not, and you have
the choice of whether or not you will subscribe to it in your code.
Anti-Rule
6: Place left braces on their own line. [ not in this class – see “format blocks
properly”]
There are
two approaches here. This anti-rule
promotes the following style:
while (someTest())
{
doSomething();
doSomethingElse();
}
As opposed
to our style:
while
(someTest()) {
doSomething();
doSomethingElse();
}
The first
form supposedly has the advantage that it is easier to match opening and
closing braces, but this is hard to believe given that in both cases, the match occurs at the previous line with the same
indenting as the closing brace. It is
just as easy to scan up to that line, whether or not the opening brace is
there.
Now, the
first case also comes at a cost: namely,
it gives up precious screen real estate for those opening braces on their own
lines. This means that you, the
programmer, get to see less of your program at any time. And this makes your program harder to
understand. Thus, we do not allow
placing left braces on their own lines.
Anti-Rule
7: main must be the only method in its class. [ not so – do what you want ]
The point
of this anti-rule is to cleanly separate a class from its use. This is admirable, but making it a rigid rule
is probably overkill. Do what you want
regarding this issue.
Anti-Rule
8: Only one class per file. [ no – you may include related private classes ]
Many Java purists argue that there should always be exactly one class per file. What is undoubtedly true, and is enforced by the compiler, is that there can only be one public class per file! However, we will allow you to include other private classes (both named and anonymous) that are only for the use of the public class in the file. If you change your mind and wish to make such a class public, it is usually not such a big deal to refactor your code and place that class in its own file.
Anti-Rule
9: Use packages, exceptions, javadoc,
junit, ant, javax.accessibility,… [ not yet ]
There are
many aspects of professional programming that are simply beyond the scope of
this course. As noted above, many of
these are in fact very important. Some
(like ant) make your life as a developer of a complex software package much
easier. Others (like the accessibility
package) allow you to satisfy legal and moral obligations to support a wide
range of users. But there are more of
these features than there are days in a year – too many to even discuss all of them, let alone actually use them in this course. We will try to discuss many of these, and use
as many as we practically can. However,
if you are interested (and we hope you are!), we encourage you to read about
them yourself. There is an amazing
wealth of online documentation on nearly all aspects of professional
development, and then a huge number of fairly decent books you can purchase as
well. To further incent you to go beyond
the course curriculum in some areas, we will provide bonus and extra credit
opportunities for you in such cases. See
your CA or instructor for details.