Programming Java with Panache
A C++ Style Manual
David B. Sher
sherd@sunynassau.edu
Mat/Sta/CMP Dept.,
Nassau Community College
Garden
City NY 11530

Style or panache is one of the ways programmers separate the professionals from the incompetents.  Good style in your programs will improve your job prospects.  Also good style in programs makes bugs  easier to find and remove.  Often using good style allows you to avoid bugs like mismatched ()’s in the first place.  Stylish programs will seem slower to write but actually they will be faster since the style will force you to understand what you are doing.

A program with panache is entirely obvious.  Each word of the program should have an obvious meaning to the programmer and the reader of the program.  The reason and necessity (or at least convenience) of each element of the program should be obvious.  Style should make every word of the program so necessary that a reader could not even think of another way of writing the same program.  Of course, this is an unachievable ideal but an attempt at an ideal leads to greatness. 

In this document I will include two programs called poor.java and GoodStyle.java.  poor.java is a program in the worst style that I could make myself write.  GoodStyle.java has the best style I could easily write.  The program with poor style compiles with no warnings or erros but does not work and has a variety of bugs. The good style program took about 1 hour to get working. 

poor.java

/* Extremely

 *poor styled simple

            program */

package Style;

public class poor {

static char

[] m38z3;

public

static

void

main(String[] z){if(m38z3[0]== // I don't know sdjw

z[0].charAt(0));int oxo=m38z3

.length;while(--oxo>0)System.out.

print(m38z3[oxo]==z[0].charAt(oxo)?

'=':'!');}}

GoodStyle.java

/*

 * class designed with good style
 * Copyright David B. Sher 2003

*/

package Style; // part collection of programs demonstrating style points

 

import java.io.*; // allows interaction with user

 

public class GoodStyle

{

    // Used to get characters from user

    private InputStreamReader ISR = new InputStreamReader(System.in);

    // Used to get lines from user

    private BufferedReader fromUser = new BufferedReader(ISR, 1);

                // collection of characters for comparison

                private char[] characters;

                /** initializes characters in class from user */

                public GoodStyle()

                {

                                try // handle any input problems

                                {

                                                int size; // number of characters in GoodStyle

                                                System.out.print("How many characters? ");

                                                size = readInt(); // size of array from user

                                                // create an array of characters of the specified size

                                                characters = new char[size];

                                                // initialize the characters in the array

                                                int index=0; // used to keep track of which character to enter into

                                                // keep getting characters from user till string is full

                                                for(;;)

                                                {               System.out.print("Enter "+(characters.length-index)+" characters: ");

                                                                String lineFromUser=fromUser.readLine(); // get line of chars from user

                                                                // transfer characters from line to array

                                                                for(int lineIndex=0;lineIndex<lineFromUser.length();lineIndex+=1)

                                                                {               characters[index]=lineFromUser.charAt(lineIndex);

                                                                                index+=1; // one more character in array

                                                                                // check if array full

                                                                                if(index>=size) return;

                                                                } // end for

                                                } // end for

                                } catch(IOException error)

                                { // output error that occured

                                  System.err.println("Error initializing: "+error.toString());

                                  characters = new char[0];

                    } // end catch

                } // end constructor

               

                /** compares array to string parameter and outputs = or !

                 * to indicate which characters are equal

                */

                public void compareTo(String comparison)

                {              

                                // traverse first argument of program and compare to characters

                                for(int index=0;(index<characters.length)&&(index<comparison.length());index+=1)

                                                // if the characters are the same then output '='

                                                if(characters[index]==comparison.charAt(index))

                                                                System.out.print('=');

                                                else System.out.print('!'); // otherwise output '!'

                }

 

                /** executable code goes here */

                public static void main(String[] args)

                {

                                if(args==null || args.length<=0) // no argument can't run

                                {

                                                System.err.println("Syntax is java Style.GoodStyle word");

                                                return;

                                }

                                else

                                {

                                  GoodStyle thing= new GoodStyle();  // gets an initialized GoodStyle object

                                  thing.compareTo(args[0]);  // compares the string to the argument

                                }

                } // end main

 

    /**Read an int value from the keyboard*/

    private int readInt() throws IOException

    {

      return Integer.parseInt(fromUser.readLine());

    }

} // end GoodStyle

Note that GoodStyle.java is much larger than poor.java.  That is because of poor style and design much was left out of poor.java such as any initialization of the class record.  Since I chose to initialize the variable with user input in Java the result was a much larger program but using panache, the resulting program is fairly clear anyway.

A)      Beautiful Indentation

i)         Keeping Lines on a Line

Indenting and alignment is required if the program will work.  I’ve seen many a student program and when the indenting is wrong they don’t work, usually they don’t compile.  The first rule is that the lines of a program should be on a single line (if they fit):

Wrong

final int m = M; float

x[m]; int p = 2;

Right

float sum = 0;  // sum of the numbers in the circularArray for calculating average

float sumSquares = 0; // the sum of the squares of the #'s in circularArray for calculating variance

There is no reason for the type float to be on the same line as the const m and on a different line from x[m].  The exceptions to this rule are:

·          Lines that are too long can be broken if:

·          You break them so that it is clear that they are incomplete.

Wrong

resultVariable = exoticMethod(x,municipalResult(y)
                + nuclearFactor(z);

Right

resultVariable =exoticMethod(x,municipalResult(y) +
                nuclearFactor(z);

·          You indent the rest of the line to indicate that it is part of the previous line.

Wrong

resultVariable = exoticMethod(x,municipalResult(y) +
nuclearFactor(z);

Right

resultVariable = exoticMethod(x,municipalResult(y) +
                nuclearFactor(z);

ii)       Lining up {}’s and other ()’s

Every {,(, or [ must be aligned with the corresponding },), or ] either horizontally or vertically. Examples of horizontal alignment are sin(x), array[index], or
for(index=0; index < size; index++) { sum += array[index]; cout << array[index] << endl;}

Vertical alignment is used for larger or more complex expressions like:
for            (
                index=0, activeValuesStart = 3;
                index < size;
                index++, activeValuesStart += 2
                )
{
                sum += array[index];
                cout << array[index] << endl;
}

Avoid code where the brackets don’t line up, even if you were copying your code from a professional source like a text book or the help documents with your compiler.  A common version of unaligned brackets is:
private void DoesNothing(){  // this method does nothing with an unimportant pointer
}

iii)      Indenting Blocks

One of the first signs of code that doesn’t work is when the code inside a method or loop is not indented.  The body of a loop must be indented from the loop to show what lines are in the loop. The body of a function is also indicated through indenting.  If a long line is broken it should be indented too.  The lines inside an if statement or switch statement should also be indented.  The case statements will not be indented but the lines in each case should be.  Lines within a block must be aligned.  Some examples of correct and wrong code are shown below.

Methods

Wrong

public static int obscure(int y)

{

       cout << “Doing something obscure to ” << y << endl;

y /= y*y+1;                                                                                      ¬should be indented

       if(y>5)return (y+1)/2;

             if(y<5) return y;                                ¬should have same indent

       return 0;

}

Right

public static int obscure(int y)

{

       cout << “Doing something obscure to ” << y << endl;

       y /= y*y+1;

       if(y>5)return (y+1)/2;

       if(y<5) return y;

       return 0;

}

Loops

Wrong

                do

                {

               cin >> x[i  // I really like this line                                               ¬should be indented

                                ]; s = s + x[ /* this line adds s to x[i] and puts the result in s */

                                  i]; s -= x[(i+m-1)%m] ;

ss += x[i]*x[i], ss

                                                = ss - x[(i-1+m)%m]*x[i+m-1-m*((i+m-1)/m)];

               cout << "Avg is: " << s/m                                                           ¬should be indented

  << "\tStdev is: " << ss/m -(s/m)*(s/m) << endl;

                } while(ss);

 

Right

                do

                {

                                cerr << "Enter New Number: ";

                                cin >> usersNumber;                // read number from user

                                sum -= circularArray[position]; // remove effect of current # on sum

                               

                                cout << "After entering " << usersNumber << " the average is " <<

                                                sum / circularArraySize;

                                // the variance is the average of the squares - the square of the average

                                cout << " the variance is " << (sumSquares/circularArraySize -
                                                square(sum/circularArraySize)) << endl;

                } while (sumSquares > 0 );                         // true only when array is cleared

 

Right

for(iterator=0;iterator<size;iterator++)                       // print and increment all elements of array

{               cout  << array[iterator] << endl;
                array[iterator]++;

}

Switch

Wrong

switch(userInput[0])
{
case ‘y’:
case ‘Y’:  // this is a positive answer
               return true;
               case ‘n’:                                                                             
¬should not be indented
case ‘N’: // this is a negative response
globalvariable++;                                                                                   
¬should be indented
return false;                                                                                             
¬should be indented
default:
               cerr << “I did not understand: “ << userInput << endl;
}

Right

switch(userInput[0])
{
case ‘y’:
case ‘Y’:                                  // this is a positive answer
                return true;
case ‘n’:
case ‘N’:                                  // this is a negative response
                globalvariable++;
                return false;
default:
                cerr << “I did not understand: “ << userInput << endl;
break;
}

Types

Wrong

// here we define a typical class it will describe a student

class student  {

       protected:    // this will contain the data.

char first[256],last[256],mi;     // name inforation

      
public:              // this defines what the user can get at

                     // constructors

// first version everything supplied

       student(char *f, char *l, char m,long id)

       { init((const char *) f, (const char *) l, m, id ) ; }

      
       // returns the first name as a constant string
       const char *first_name() const

                     { return first; }   

       
};

Right

// here we define a typical class it will describe a student

class student 
{

protected:           // this will contain the data.

       char first[256],last[256],mi;     // name inforation

      
public:              // this defines what the user can get at

                     // constructors

       // first version everything supplied

       student(char *f, char *l, char m,long id)

              { init((const char *) f, (const char *) l, m, id ) ; }

      
       // returns the first name as a constant string
       const char *first_name() const

                     { return first; }   

       
};

B)       Clear Comments

Comments tell you what a program is about, the code tells you what it does.  Comments are about why a line is in your code.  Only obscure or confusing code require a comment to tell you what the code does.  For example:

// the % makes the array circulate back to 0  when pos is circularArraySize -1
return (pos + 1)%circularArraySize;

is an example of obscure code that needs to be explained.  Usually code is clearer than the above and what it does needs no further explanation.

In general you should not wait till you have completed the code before you comment it.  Writing a comment on code makes you stop and think about what you are doing.  The thinking allows you to avoid bugs before you write them so the coding would go faster.  Also if you want to show your code to the instructor or to a friend, having comments will mean that you don’t have to explain each line. 

i)         Initial Comments

Every class file should begin with a large comment (delimited with /** and */) that discusses the purpose of the code in that file.  If there are multiple files in the program it should discuss how this file relates to other files in the program.  It should name the author of the program, often with a copyright notice. If this is the file containing the main function then the initial comment should discuss the behavior of the program.  An example of a good initial comment is:

/**

                This class is meant to demonstrate how a well written program can

                make a complex idea clear.

                Copyright David B. Sher 1998

*/

ii)       Commenting Variables

When introducing (declaring) a variable into a method it is necessary to explain why you need this variable.  If you need more than one line to describe a variable then your variable is doing too much.  A variable should have a simple easily explained purpose.  You should explain this purpose when you write the variable declaration.  If you can not write a simple comment explaining the variable then you need to think more before you write more code.  Global variable are particularly important to comment since they are used throughout the program.

An example of variable commenting is:

Wrong

const int m = M; float                               ¬every variable and constant should have its own line

x[m]; int p = 2;                                         ¬every variable should have a comment

Right

float sum = 0;                           // sum of the numbers in the circularArray for calculating average

float sumSquares = 0;              // the sum of the squares of the #'s in circularArray for calculating variance

int position = 2;                        // starting position in circular array

 

When declaring a class variable it is doubly important to specify why it is there.  A class variable usually will be accessed from many of the methods of the class.  Such a variable also persists as long as the object containing them persists or if they are static they persist as long as the program exists.  In any of these cases a class variable must have a comment explaining why a persistent primitive type variable or persistent reference to an object is required.

iii)      Commenting Methods

Every method should have a comment that explains what a method does and how it uses its parameters.  Of course, the comments must make sense unlike:

// rubber baby bunker mice

int main()

Beyond that minimal level a comment should be sufficient so that a stranger can call your function without having to read the code, just the comment.  Focus on the purpose of the function rather than how it works, for example:

// This method squares a number to help calculate variance

public static float square(float number) { return number*number; }

 

iv)      Commenting Loops

Loops are used in programs either to traverse or search a data structure (like an array) or to interact with the user, usually.  If you are traversing or searching a data structure you must explain which data structure is being searched or traversed.  You must also explain why you are doing this.  If it is a user interaction loop a shorter comment is usually sufficient since such loops tend to be self explanatory.  If the loop is used for a more exotic purpose you should give a more extensive explanation since such a use of a loop will be surprising for experienced programmers.

Some examples of comments on loops are:

// Copies first N elements from array1 to array2

for(index=0;index<N;index++) array1[index]=array2[index];

 

// copies elements from linked list into an array

for(linkPointer = list->next, index=0 ; linkPointer != NULL ; linkPointer=linkPoint->next,index++)
                array[index]=linkPointer->data;

v)       Commenting Conditionals

Conditionals are if and switch statements.  Often the actual condition in an if statement does not perfectly clarify why you are testing the conditional.  For example:

if(list->next != null)   // then the list has no elements to act on

                doSomething();

else                          // there are elements in the list

It is usually necessary to examine each case in a switch statement and what it means.  This comes up particularly often when parsing input:

// determine the command from the first letter in the word

switch(command[0])

{

case ‘a’:

case ‘A’:                   // the activate command

                Activate();

                break;

default:                    // can’t figure out which command you want to execute

                cerr << “I do not understand “ << command << endl;

}

vi)      Commenting Lines

Many other lines require comments to clarify them.  Any line that is not a simple print statement or equally obvious should have a comment explaining why the statement is necessary in the program.  If you don’t know why then you are programming too quickly.  Unless the line is quite unclear you need only express why the line is there and not what it does. 

Wrong

Array[x] /= 2;                                                                                 ¬this line needs a comment

cout << “Lets boogie\n”                    // prints a line which says lets boogie     ¬this line doesn’t

x++;                                                 // this line adds 1 to x ¬this comment doesn’t explain why

Right

size++;                                             // after inserting the element increase the size of the list

cout << “Copyright David B. Sher 1998”

 

C)       Names of Distinction

Methods, variables and types need names that (in order of importance):

1.        indicate the purpose of the item,

2.        are easily remembered,

3.        and are unique.

If a local variable is meant to index the array you can call it index.  If there are several array parameters to the function – inputArray, outputArray - for example, you can use indexInputArray as a variable name.  Variables names can contain several words which are separated by capitalization or by _’s (user_name).  More than two words in a variable name can make it too difficult to remember.  Avoid words that are difficult to spell, like receive.  There is a custom of using one letter variable names for indices, like array[i].  Such names are common but should be avoided unless you are implementing mathematical formulai.  Similarly unless you are referring to a position on the X coordinate avoid using the name x for a variable. 

Primitive type variables should be singular nouns since they represent a single measure count or character.  Arrays usually should be plural nouns.  Methods should be verbs since they represent an action their object can perform.  Classes and interfaces should be category nouns representing the idea that a class or interface  represents. 

The names of all class variables and methods should start with a lowercase letter.  Classes and interfaces  should start with capitol letters.  This allows one to instantly recognize whether a name is a type or part of an object. Packages start with small letters also. 

The general rule is: variable names should be words or word combinations containing ordinary vowels and consonants.  This allows you to talk about your program with professors, bosses, and colleagues.  It is difficult to discuss how the variable mxtplztz’s value changes in each iteration of a loop. 

Avoid:

·          Giving any variable the same name as a type.  For example: float float;  struct link link;

·          Giving a local variable the same name as a parameter (will cause ’s for sure).  For example:
void deleteElement(list input, int pos)
        {               float pos;  // this is a
 because the parameter pos can not be used in the program now.

·          Giving a local variable the same name as a global variable.

·          Giving two local variables the same name unless they do exactly the same thing. 

D)      Lost at C

C, C++ and Java were designed to be programmed by experts, code written in these languages could not avoid being obscure.  Many features of this language were designed to encourage efficiencies no longer important in modern machines and programs.  A good programmer can avoid using obsolete and confusing language features.  Some language features offer advantages the designers did not imagine.  You show panache by taking advantage of features that improve and avoiding the rest. 

i)                     Punctuation Difficulties ?:,&|

There are several obscure uses of punctuation marks in Java that can confuse novice or distracted programmers.  In particular, the , should be avoided except as a parameter separator.   Other legitimate uses for ,’s are separating declarations and using inside a for loop where ;’s are not allowed. 

Wrong

if(x>3,x<7) …                       // pointless test of x against 3

Right

max(x,y)                                               // , separates parameters

Right

for(int first=0,last=size-1; first<size; first+=1,last-=1)

Another obscure piece of C syntax is the ?: combination. This is used as an if statement when you can not use the if statement.  Avoid using this syntax since it is obscure and often confusing. 

The single & and | punctuation marks are used for bit manipulation.  Unless you are masking bits avoid using this syntax.  If you don’t know what masking bits are then you shouldn’t use these operators.  Also >> and << should only be applied between integers when you actually are doing bit manipulations. Avoid using >> and << for integer multiplication and division.  Usually you can rely on your compiler’s optimizer for such simple tricks. 

ii)                   ++ -- Madness Madness

Whenever ++ or -- appear in a program it causes a side effect which can result in unexpected program behavior.  For example temp=array[index++]; mysteriously  changes the value of index while it also accesses an array.   Combining many operations on one line might be a fun game but the pain of unscrambling the confusing code that can result is simply not worth it.  Most of what we use ++ for can be done as easily with +=1.  Hence, temp=array[index++]; is the same as temp=array[index]; index+=1; but less obvious; and we must all strive to be obvious! 

E)       Organized Programs

i)         Organizing Methods

Clear programming requires organization of the code.  In particular people have limited attention spans and can best focus on small items.  Java allows us to break up complex ideas into smaller structures.  Focus on how one can make the items in your program smaller and simpler.  Avoid or break up methods that take more than 15 lines to write.  

Methods that do several tasks should call methods that do simple tasks.  Often simple methods can be used in many parts of the program.  Such methods should be public and, if appropriate, static.  Some methods  should be dedicated to the user interface or interfacing with the file system.  There should be a main method that calls these when necessary.  The other methods should avoid any file system or user interaction.  This allows these methods to be used many different user interfaces.  Even error messages can be handled with throws. 

If you organize your program this way you will have a more organized program and you will be able to reuse your carefully written classes.

ii)       Lots of static

If a method never uses any of the class variables or non-static methods of its class it should always be static.  However  if most of the methods you are writing are static perhaps there is something wrong with the class organization.  The purpose of a class is to hold and manipulate persistent data which is encoded as class variables.  There may be a few classes in a project like the Math class in java.lang whose purpose is collect static objects and packages.  However usually use of a lot of static data and methods shows that the programmer is not thinking in an object oriented manner.

 

iii)      Neatly tied packages

Classes whose purpose is related such as classes for sound output or classes for special effects should be gathered into packages.  Each package should have a name indicating what kinds of services the package provides.  All the packages designed for a project should be subfolders of the project folder.  Having packages within packages such as java.awt.events just makes the job of the programmer harder.  

iv)      Use interfaces whenever possible

Interfaces allow classes from a variety of different packages to fit into the same variable.  An interface describes what methods and variables a class requires but not how it implements the methods.  Interfaces are used the same as classes and are very useful for collection types.  Generally when you need to use an object of a different type than your class it is better to use an interface since a wide variety of classes can fit that description.

F)       Writing Java with Class

Classes in Java allow you to design types for advanced data structures.   It gives you to features to make these types operate almost like built in types.  This power can result in beautifully clear code or incredibly confusing code.  If you design well your classes will be clear and easy to use. 

i)                     Private, Protected and Public

All data members (members that are not functions) should be protected. Avoid private members since that makes derived members behave differently than members of the type.  Member functions that can only be called from members should also be protected. 

ii)                   Class Variables

Class variables are members of a class that contain data rather than methods to manipulate the data.  The data members should be protected or private to prevent inconsistent data in the class.  If a local variable suffices do not create a data member.  The data members should hold data that persists and is manipulated by several member functions. 

G)       Panache

No one is perfect.  No one writes perfectly clear programs.  Even the example above has flaws.  If you are a perfectionist, think twice about writing a program, they are intolerably imperfect.  Do not let this stop you from writing the best program you can.  Do not let poor examples that come with your compiler or your text book dismay you.  Writing with panache will result in programs that are fast to write and easy to read.  You will be able to use repeatedly reuse your code and save a lot of time.