Programming Style
What constitutes good programming style is somewhat subjective. These guidelines are largely Java-centric, but most of the principles apply equally to other languages.
See also:
- Oracle Java code conventions (no longer maintained)
- Google Java style guide
Readability
Code should be easy to read.
- use white space generously
- use spaces rather than tabs, so that code display doesn’t depend on the reader’s tab settings
- use indentation to indicate block structure
- avoid long lines that may be hard to read on a narrow screen
- pick a sensible style for formatting names
- follow common conventions
- do all of these consistently throughout the program
- use auto-formatting tools, since they can do some of these for you
Clarity of Intent
It should be easy to understand what the programmer means.
- try to write code that is as simple as possible
- use meaningful names for variables, methods, classes etc
- write Javadoc documentation for public methods, classes and packages
- write internal comments where they are informative, but not just for the sake of it
- don’t use ‘magic numbers’ that make the reader guess their significance; define as constants with meaningful names
Clarity of Structure
It should be easy to see how the different parts of the program fit into the whole.
- break up long classes and methods into smaller ones
- use braces even for single-line blocks, since it’s easy to misinterpret the structure otherwise
- avoid deep nesting of loops and conditional tests
- make fields and methods private unless they need to be accessed from other classes
Correctness and Maintainability
Don’t duplicate code. Doing so makes the program longer, and more importantly harder to maintain since one copy of the code might be changed and become inconsistent with the other.
- factor out code duplicated in methods into a common method called by both
- factor out fields and methods duplicated in classes into a superclass extended by both
Modularity
Write modular code. Different concerns should be handled by different classes, and where possible even different methods within one class. This makes code easy to understand and maintain, and it allows one to reuse existing code for new purposes. This applies also to the distinction between domain logic and I/O. A method that does a complicated calculation and also prints the result to a file cannot be reused for purposes that do not involve printing of the result.
Examples
A Full Class
Here is a class written in a common Java style, illustrating a number of the style guidelines. You won’t be penalised if you prefer a different style and use it consistently, but tutors know that this style is considered acceptable.
/** * This example shows how a thrown exception is passed back to the calling method, and * to the method that called that one, and so on until a catch clause is found or the main * method is reached. * * @author 0123456789 */ public class Example { public static void main(String[] args) { try { Example example = new Example(); int amount = example.getAccountBalanceByName("John Mcleod"); } catch (UnknownCustomerException e) { System.out.println("Couldn't find id for customer"); } } /** * Gets the account balance for the customer with the given name. * * @param customerName the name of the customer * @return the current account balance of the customer * @throws UnknownCustomerException if a customer with the given name can't be found */ public int getAccountBalanceByName(String customerName) throws UnknownCustomerException { int id = getId(customerName); return getAccountBalanceById(id); } private int getId(String customerName) throws UnknownCustomerException { // In a real implementation this would look up the name in an array, database etc. if (customerName.equals("Jane Smith")) { return 32576; } else { throw new UnknownCustomerException(); } } private int getAccountBalanceById(int customerId) throws UnknownCustomerException { // In a real implementation this would look up the balance in an array, database etc. return -10; } }
This example illustrates:
- single spaces between most reserved words, identifiers and symbols
- single blank lines between methods
- ‘camelCase’ style for identifiers
- indentation showing block structure
- Javadoc comments for class and public methods
- internal comments where necessary to clarify intent of code
- if/else branches contained within braces even when a single statement
- short methods
- methods private unless they need to be used from outside the class
Comparing Values
Booleans
The following code fragment, testing the value of a boolean variable condition, is correct but inelegant:
if (condition == true) {...}
Since condition is boolean, you don’t need the equality check. This means exactly the same:
if (condition) {...}
Similarly, rather than
if (condition == false) {...}
it’s simpler to write:
if (!condition) {...}
Strings
This code fragment may give surprising results:
if (s == "hello") {...}
Depending on how s has been defined, the conditional block may not be executed, even if s does contain the characters hello. This is because the code is checking whether s is exactly the same string object as "hello". Instead, write:
if (s.equals("hello")) {...}
Assignment versus comparison
This fragment is valid but probably doesn’t do what the programmer intended:
if (condition = true) {...}
The conditional block will always be executed, regardless of the value of condition. This is because the = operator means assignment not equality, so this updates condition to the value true, and then uses that value in the comparison.
Simplicity
Nested tests
The double test in this example is unnecessary:
if (condition1) if (condition2) doStuff();
since the checks can be combined:
if (condition1 && condition2) { doStuff(); }
Note also the braces to make the control flow obvious even if indentation is changed.
Conditional boolean expressions
The code in this example is more complicated than it needs to be:
if (f1(i)) return true; else if (f2(i)) return true; else return false;
The explicit comparisons can be eliminated to give:
return f1(i) || f2(i);
Clarity of nested comparisons
A single cascaded comparison tends to be clearer than a series of nested comparisons, for example:
if (f1(i)) return g1(i); if (f2(i)) { if (i > 0) return g2(i); else return g3(i); } else return g4(i);
is probably clearer as:
if (f1(i)) return g1(i); else if (!f2(i)) return g4(i); else if (i > 0) return g2(i); else return g3(i);
Unnecessary variables
The code in this example is more complicated than it needs to be:
boolean val = true; boolean stop = false; while (i < n && !stop) { if (f(i)) { val = false; stop = true; } i++; } return val;
Both of the boolean variables can be eliminated by allowing the method result to be returned from within the loop:
while (i < n) { if (f(i)) return false; i++; } return true;