Coding Style

Advanced Programming/Practicum
15-200


Introduction This lecture discusses coding style, including why using good style while writing our programs can help us debug them faster. We will discuss in detail four important aspects of style: names, alignment, locality, and comments. Most decisions that we make about formating our programs closely involve one of these four aspects. You should also start reading in Vermeulen, The Elements of Java Style. Specifically, this lecture covers items 5-15, 25-26, and 32-37 (which appear on pages 5-18, 25-26, and 31-36 in Vermeulen's book).

Coding Style Programmers spend an enormous amount of time reading and studying code when they are writing, testing, and debugging their programs. Using good programming style allows this process to proceed much more easily. When writing a program using iterative-enhancement, it is an excellent idea to beautify your code at the end of each enhancement, before proceding to the next one; each enhancement should be the best thatit can be, before continuing. Ultimately, this strategy will save you time compared to the strategy often used by students: ignore style until the program is completely written. This is a penny-wise, pound-foolish strategy. It is much harder to "finish" a poorly-styled program, because it is harder to read and understand it; (software) engineers must learn to practice techniques that overcome human nature; this is one example.

In the real world, companies have their own style guidelines, which all their programmers must follow (see Vermuelen's book). In this way, code written by different programmers is consistent (and therefore more easily readable by other programmers). So, it is not unreasonable for me to ask you to write in a certain style, as consistently as you can.

We will use four general principles to discuss issues in programming style (backwards, they are the acronym CLAN).

  • Names
  • Alignment
  • Locality
  • Comments

Good Names Programmers get to choose identifiers that name variables (and as we will see later in this course methods, parameters, classes, exceptions, etc). We should choose descriptive names. Yes, we should also try to choose short names, but descriptiveness is more important. A long descriptive name is better than a short unclear one. Of course, a short descriptive name is optimal.

Beginning programmers typically choose names that are too short: they abbreviate too much, or use just single letter names. Rather than declaring

int qs; //qs means quarters
declare a variable named quarters (and then, if necessary, comment on some other aspect of the name -like its units). Using longer names requires a bit more typing (which costs some time) and takes longer to read (ditto) but it makes it much easier for you to understand your program as you are enhancing/debugging it (which saves much much much more time). Examine the names that I use in my sample and solution programs and mimic them.

So far, we have learned the fllowing Java naming conventions.

  • Names of Variable start with lower-case letters, and use upper-case letters at the start of each word in the name ("camel-style"): e.g., dartsInCircle.
  • Names of classes start with upper-case letters,and use use other upper-case letters at the start of each word in the name: e.g., StringTokenizer.
  • Names of public static final fields are written in all upper-case, and use underscores to separate each word in the name: e.g., SPEED_OF_LIGHT.

Alignment
(indenting)
Generally, we use whitespace to present our programs (to humans, not computers) in an easy to read and understand form. Remember that adding extra whitespace doesn't affect the meaning of our programs (the sequence of tokens is still the same), but it does affect how a program is displayed in the editor while we are reading it.

Using extra whitespace will make the program "longer" but easier to read. In fact, in one early style of written English (scriptio continua), words were strung together with no intervening whitespace. Itwasstillreadablebutveryslowanddifficulttocomprehend. Sometimes smaller isn't simler.

Alignment involves mostly using horizontal whitespace. The most important use of alignment is showing which statements are controlled by which control structures, with the controlled statements indented to appear inside the statements that control them. This relationship is the essence of using control structures, so highlighting it is critical.

There is a pattern in how we write control structures. For example in the block after main(), all statements are indented at the same level.

  {
     statement1
     statement2
     ...
     statementn
  }
A typical indentation for these statements (and others inside control structures, illustrated below) is 2-4 spaces: one space is too little and more than four is too much (the Goldilocks principle again). In fact, the indent icons in the editor (red left-arrow followed by text or red right-arrow followed by text) make it easy to select multiple lines of text and indent (or outdent) them 2 spaces at a time.

Likewise, in an if statement we use the following forms (depending on whether or not the statement contolled by the if is a block)

  if (test)
    statementT

  if (test) {
    statementT1
    statementT2
    ...
    statementTn
  }
For an if/else statement, there are four possiblities (based on the absence or presense of blocks). From simplest to most complicated, they are:
  if (test)
    statementT
  else
    statementF


  if (test)
    statementT
  else{
    statementF1
    ...
    statementFn
  }


  if (test) {
    statementT1
    ...
    statementTn
  }else
    statementF


  if (test) {
    statementT1
    ...
    statementTn
  }else{
    statementF1
    ...
    statementFn
  }
I like to write }else{ on the same line, but Vermeulen likes to write
  }
  else{

Many programmers adopt a style that ALWAYS use blocks in if statements (and loops), even if they contain just ONE statement. On the positive side, such an approach makes it very easy to add/remove statements (when debugging/enhancing programs), because the block is already there; otherwise going from one to more statements requires adding a block, and going from multiple to one statement requires removing the block. On the negative side, blocks, when they are unneccessary, make the program a harder to read. So, choose whichever of these options you think is better, but be consistent with your choice. I like "blocks where necessary" but Vermeulen likes "always blocks".

Finally, identically to if statements, we align a for loop by indenting the statement that is their body.

  for (;;)
     statement

  for (;;) {
    statement1
    statement2
    ...
    statementn
  }
Almost all interesting loops use a block for their bodies. Exceptions are very simple loops and loops that have one try-catch statement in their bodies; such a statement has most of its code in a try block.

I cannot overemphasize how important it is to use proper alignment in control structures. A major source of programming errors for beginners is not understanding which statements are controlled by which control structures: these can get tricky with expression statements inside if statements inside loops. Proper alignment makes such relationships much simpler to see. I have seen students spend 2 hours trying to debug a program; at which point then finally spend 10 minutes aligning its statements (because I refuse to help them until they do), and then they solve their problem by themselves in 1 minute. If you expect to debug your programs, it is imperative that you use proper alignment whenever you add/remove code to/from them.

Another use of alignment occurs when declaring a sequence of variables; rather than doing so haphazardly, we can align the types, names, initial values, and comments.

  int    game      =  0;    //Current game being played
  int    maxGames  = 10;    //Limit on games for one customer
  int    winCount  =  0;    //For statistics (see WL_Ratio too)
  int    loseCount =  0;
  double winLoseRatio;      //Calculated at the end of a session
Some programmers think that this kind of alignment is too much trouble, because if you add/remove declarations, you must realign them; I think the effort is worth it. So please examine all the alignment that I use in my sample and solution programs and mimic them.

Locality
(paragraphing)
Locality is the most subjective of the style rules. It involves mostly adding extra vertical whitespace (blank lines). By grouping statements together and then placing blank lines between groups, we create the programming equivalent of paragraphs in prose writing (where each paragraph contains related sentences). In a written paper, students would never put all the sentences into one long paragraph; likewise, students would never make every sentence into its own paragraph. So, we should always use a more reasonable grouping (some number of related lines) for paragraphing in our programs.

Typically, each code group should contain a half-dozen statements. The magic number 7+/-2 is also used for psychological reasons: it represents the number of items typically usable in the brain's short-term memory. Whenever a large number of statements appear in a block of code, use blank lines to group them into a smaller number of related sequences. We can write a preface comment (see below) that acts as a topic sentence for the paragraph of code.

A for loop and try-catch almost always start their own group; so do complicated if statements. Locality is more art than rules; I encourage you to examine the groupings that I use in my sample and solution programs and try to critique and ultimately emulate them.


Comments We document our programs with comments. While we try to express ourselves as well as we can with Java code, there is always other useful information about a program that we would like communicate to programmers reading our code (including ourself, while debugging it, or at some future date when we are enhancing our code). Such information is for programmers, not the computer: not the instructions saying HOW the code works (that is for the programmer and computer), but WHAT the program does and WHY it does it (that way). We supply this information in comments.

There are a few different categories of comments that frequently reappear.

  • Preface comments act as a topic-sentence, describing a group of related statements that directly follow the comment. Use the locality principle with such comments: there should be more blank lines separating the comment from the code before it (which it doesn't describe) than blank lines separating the comment from the code after it (which it does describe). Taken together, and indented appropriately, these commments provide an outline of the program. Every loop should have a preface comment; for other statements, comment them as necessary.

  • Sidebar comments appear on the same line, after some statement. They help explain that statement; sometimes a series of sidebar comments will also help outline the computation. Use alignment so that all the sidebar comments are aligned: that makes it very easy to have the code separated from the comments (more use of the locality rule).

  • "Sandwhich comments" directly preface and suffix some statement (with no blank lines lines. Use a sandwhich comment to make the if/break; statements terminating a long loop easy to locate.
      //////////////////////
      if (index == maxIndex)
        break;
      //////////////////////

  • Avoid mingling comments within code; separate them for clarity. In the following example use the FORMER side-bar comment, not the latter, code.
      d = v*t;  //distance = velocity times time
      d /*distance*/ = v /*velocity*/ * t /*time*/;
      
Like the other rules of good style, comments are best included while the program is being written, not after it is working. I find and correct many errors while writing comments, because I am focusing on the code while writing about it. Again, many students approach writing comments as something to do AFTER the program is complete, which ultimately slows them down. Examine the comments that I use in my sample and solution programs and try to critique and ultimately emulate them.

Miscellaneous Style Rules Finally, here are some miscellaneous style rules
  • Use local variables whenever they clarify the code, keeping expression sizes managable; use the goldilocks principle
  • Don't reuse variable names for more than one purpose.
  • Choose the types for variables carefully. If a variable stores only integral values, declare it to be an int; use explicit conversion if you need to use it as a double in some expression(s).
  • Initialize variables when they are declared; but don't initialize them at all if the next use of the variable is to store something into it.
  • Use about 80 characters per line; remember that a carriage return is whitespace, so don't write huge lines of code.
  • Good style is cumulative: each style improvement may marginally improve a program; but many can dramatically improve it.
Write code to be easily readable and understandable. Don't obfuscate code because you think it will make the code run faster. Compilers do amazing optimizations.

Problem Set To ensure that you understand all the material in this lecture, please solve the the announced problems after you read the lecture.

If you get stumped on any problem, go back and read the relevant part of the lecture. If you still have questions, please get help from the Instructor, a CA, or any other student.

  1. Each of the following blocks computes the average of the scores entered by the user. Notice the difference between where value is declared and initialized. Which code do you think is best? Explain why.
      int count = 0;
      int sum   = 0;
      int score;
      for (;;) {
        score = Prompt.forInt("Enter score (-1 to terminate)");
        if (score == -1)
          break;
        count++;
        sum += score;
      }
      System.out.println("Average = " + sum/count);
    
    
      int count = 0;
      int sum   = 0;
      for (;;) {
        int score = Prompt.forInt("Enter score (-1 to terminate)");
        if (score == -1)
          break;
        count++;
        sum += score;
      }
      System.out.println("Average = " + sum/count);

  2. Does Java allow any of the following code fragments (with a redeclaration of v). If so, what values are printed.
    
    a){
        int v = 0;
        System.out.println(v);
        {
          int v = 1;
          System.out.println(v);
        }
        System.out.println(v);
      }
    
    
    
    b){
        {
          int v = 0;
          System.out.println(v);
        }
    
        {
          int v = 0;
          System.out.println(v);
        }
    
      }
    
    
    c){
        {
          int v = 0;
          System.out.println(v);
        }
    
        System.out.println(v);
    
        {
          int v = 0;
          System.out.println(v);
        }
    
     }
    
    
    d){
        {
          int v = 0;
          System.out.println(v);
        }
    
        {
          System.out.println(v);
          int v = 0;
          System.out.println(v);
        }
    
      }