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:
   // This is an array of Strings holding the names of students,

   // and it is in ascending order

   String[] studentNames;

And a better way:
  
String[] studentNames;  // in ascending order

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(),
                 someOtherMethodCall());

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.

Another good use of whitespace is to align lines that repeat but with subtle variations.  By aligning the similar parts of the lines, the parallel structure of the code becomes more apparent:

   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)·statement;
Or:

   if·(test)
      statement;

Or:
   if·(test)·{
      statement;
   }


You can only use the first case (all on one line) if the statement is short and clearly fits on the one line.

This same general format applies analogously to
for, while, do, switch, and try-catch statements.

       for methods

Use this format to call a method:
   foo(x,y,z);
Or (for qualified member methods):
   someObject.foo(x,y,z);
Or (for qualified static (class) methods):
   SomeClass.foo(x,y,z);
In particular, do not place a space between the method name and the parenthesis.  So this is incorrect:

   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:
   someObject.foo(x,·y,·z);

       for expressions

Generally, place one space on either side of binary operators and nowhere else, as in:
   x = ((-z % q) / Math.sqrt(Math.abs(4 – y)));

       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:
   while ((x = readInt()) < 0)

      ; // 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:
   switch (month) {

      case APRIL:

         fileAnnualTaxes();

         // fall-through

      case JANUARY:

      case JULY:
      case OCTOBER:
         fileQuarterlyTaxes();

         break;

   }

Declare array types

There are two ways to declare arrays in Java:
   int[] x;
Or:
   int x[];
These mean the same thing.  However, consider the following:
   int[] x, y;  // x and y are arrays
And:

   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:
   int[] x, y;  // ß Right!

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:
   class Foo {

      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:
   // Wrong!

   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:
  float f = foo();

  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:
  if (s == "foo") doThis();  // ß Wrong!

You should use:
  if (s.equals("foo")) doThis();  // ß Right!

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
Note:  In some assignments, you will be explicitly instructed to write code that does not have a user interface.  These guidelines, of course, do not apply in those circumstances.

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.