Code style
Software engineers spend more time reading code than writing it, so it is important to pay attention to the style of your code in addition to following the syntax rules. While some elements of style can be subjective, others are justified because experience has shown that they reduce bugs and make code easier to review and maintain. Above all, coding style should be consistent within a project or organization. (If you are interested, you can read more about the benefits of style guides in Chapter 8, Style Guides and Rules, of Software Engineering at Google.)
In CS 2110 we expect you to follow sound stylistic practices when writing code, especially code submitted for programming assignments. This means choosing clear variable names, ordering and decomposing computations logically, minimizing scope and access, and writing thorough specifications and sufficient implementation comments. It also means adhering to our class formatting rules. Fortunately, the latter can be handled automatically by IDEA (see formatting setup, below). Here are the rules we expect you to adhere to in this class:
Naming conventions
The following conventions were adopted by Java’s standard library and are used by the vast majority of Java code worldwide.
- Class names start with a capital letter, and each subsequent word in the name is capitalized (this is called CamelCase). Underscores are not used. Class names are almost always nouns.
- Examples:
Student
,ExamQuestion
- Examples:
- Method names start with a lower-case letter, and each subsequent word in the name is capitalized (lower camelCase). Underscores are not used. There is no distinction between private and public method names.
- Examples:
name()
,deductPoints()
- Examples:
- Field names and local variable names also use lower camelCase, just like method names. Underscores are not used. There is no distinction between private and public fields, or between fields and local variables.
- Examples:
major
,gradePointAverage
- Examples:
- Constants (
static final
fields) are named in all caps with words separated by underscores.- Examples:
PI
,SPEED_OF_LIGHT
- Examples:
- Package names are all lower case. Each component of the name (separated by periods) corresponds to a subdirectory when saving source code files for classes in that package. They often incorporate a “reverse URL” to ensure they are unique (we will break that convention in this class to reduce the number of subdirectories in your projects).
- Examples:
com.sun.mail.smtp
,edu.cornell.cs2110.a7
- Examples:
Naming guidelines
Names of classes, methods, fields, parameters, and local variables should be descriptive. Concise names are preferred, but not at the expense of clarity. Somebody else should be able to look at an arbitrary line in the middle of your program and have a sense for what is going on. Avoid very short (1 and 2-letter) names for anything except possibly array indices. In unit tests (whose code can be very repetative), it is more acceptable to sacrafice some clarity for the sake of concision.
- Bad variable names
n
,s
,p
,cs
- Better variable names
numAttendees
,student
,previousNode
,children
In some situations, such as improving readability of mathematical formulas, it may be acceptable to introduce local variables with short names as long as there are comments explaining what each variable represents. (See contradictory advice for more details.)
Documentation
Every class must have a JavaDoc comment explaining what it is supposed to represent (and in what context). Every method must have a JavaDoc comment providing its specification. Method specifications must document preconditions, return values, side effects, all parameters, and any checked exceptions. Every field of a class must have a JavaDoc comment describing its interpretation and role in class invariants.
If you pick good names, some of this documentation might feel redundant; write it anyway. Always keep in mind “what vs. how”—specifications for public classes and methods live on a different side of the abstraction barrier from their definitions, so they should not feel redundant with the implementing code.
Formatting
Rather than listing out detailed formatting rules, we ask instead that you install our course style scheme in IntelliJ and let it format your source code for you. Here are some of the high-level things it is enforcing:
- Blocks must be indented according to their level of nesting. Each nested level is indented an additional 4 spaces. Space characters are used for indentation, not tabs.
- Bodies of control structures must always be enclosed in curly braces, even if they only contain a single statement.
- The opening brace of a block goes at the end of the line containing the declaration or control structure associated with the block. The closing brace should be indented to align with the start of that declaration or control structure (so unindented one level relative to the body).
- Spacing should be consistent, with a single space after control structure keywords and between identifiers and operators.
- Example:
if (width < height) { /* code */ }
, notif(width<height){/*code*/}
- Example:
- Lines may not be more than 100 characters long (ideally less than 80).
- Exception: Comments containing URLs
Read Google’s Java Style Guide for more examples, along with justifications.
Formatting setup
Please download our code style scheme (right-click, Save link as… or Download Linked File) and import it into IDEA using the following procedure:
- Open the IDE settings by selecting File | Settings (Windows, Linux) or IntelliJ IDEA | Preferences… (Mac).
- In the list on the left, select Editor > Code Style.
- Next to the Scheme drop-down box, click the gear icon, then select Import Scheme > IntelliJ IDEA code style XML.
- Browse to your download of intellij-java-cs2110-style.xml and click OK. Then click OK in the subsequent dialog.
- Ensure that CS2110Style is selected in the Scheme drop-down box, then click OK to close the Settings dialog.
(For reference, our formatting rules are based on Google’s Java Style Guide, but with a block indentation of 4 spaces, which is more common. Read their style guide to learn about their justifications as well as some higher-level guidelines that cannot be enforced by auto-formatting.)
Now, when you are editing some code, you can select Code | Reformat Code to apply the style rules to the current file. Take note of the keyboard shortcut so you can do this quickly as you write. You can also configure IDEA to automatically reformat your code whenever you save it. Go to File | Settings, then select Tools > Actions on Save, then check Reformat code and click OK.
Practicing good style (both low-level formatting and higher-level guidelines) is to your advantage for more than just style credit. It will make it easier for consultants to diagnose problems in your code, and it will make it easier for graders to evaluate your submissions. You may also find that it’s easier for you to pick up where you left off when working on a long assignment. With code as with English, don’t let poor presentation undermine good content.
Contradictory advice
Style is a balancing act of tradeoffs and constraints, some of which change over time. This can lead to contradictory advice, even from people trying to give justified, objective advice for a similar context. For example, JavaHyperText’s style rules are sometimes at odds with this semester’s requirements. While there is value in mimicking established conventions for common idioms, many prevalent stylistic features can no longer be justified and should be abandoned.
Here are some examples of stylistic “anti-patterns” and why they are no longer recommended:
Short variable names
The world is full of code using extremely short names for parameters and variables, often just a single letter. It’s not hard to see why—it requires less typing, it helps avoid awkward line breaks, it mimics mathematical notation, and naming things is hard. But modern style guides often discourage such variable names, as they make it difficult to skim code and can more easily mask bugs during review.
Historically, the length of source code was a major concern: computers had very limited memory and storage, and punch cards imposed strict limits on line length. Early code adopted an extremely abbreviated style in order to cope, but these constraints are no longer applicable. Two others, though, still contribute to the contemporary use of short variable names, especially in educational settings: textbooks and slides. When forced to fit a snippet of code on a page or (legible) slide, abbreviating identifiers is a useful tactic (for an example, see Numerical Recipes). The problem is that students propagate this tactic to production codebases, where these constraints do not apply. Concision is good to a point, but identifiers consisting of three or fewer characters typically go too far.
For CS 2110, please follow these guidelines when choosing names:
- Public interfaces (e.g. public methods, constants, types) should always have a meaningful and unambiguous name.
- Method parameter names should also be meaningful and unambiguous, even though they are not technically part of the method’s signature. Don’t make the excuse that parameter names are irrelevant because they are defined in the method spec (JavaDoc)—this does not lesson the importance of good names when skimming code.
- Local variable names may be abbreviated if their use is confined to a small scope (i.e. <10 lines), they are mimicking mathematical notation that serves as the primary specification for a technical algorithm, or they are part of a legacy idiom with little significance on their own. For example,
for (int i = 0; i < v.length; ++i)
is okay when iterating over arrays, but preferfor (Node child : children)
when applicable. - Abbreviated type parameter names (e.g.
T
) are okay when only one type parameter is present and its role in the ADT is obvious. Otherwise, use descriptive names (e.g.KeyType
andValueType
for aMap
). - If good names makes it difficult to spot patterns in intricate code, use an alias to shorten the name for a limited scope. For example, you can assign a parameter’s value to a new local variable with an abbreviated name to use in a loop. Some languages even allow importing public interface identifiers under an alias (e.g. “import as”), though this should always be restricted to as small a scope as possible.
Type annotations in names
Code from a certain era, especially C/C++ code, often added prefixes or suffixes to variable names to annotate whether they were a member variable, whether they were a pointer, etc. (this is referred to as “Hungarian notation”). Nowadays, syntax highlighting and context tips in IDEs can convey this information without the drawbacks of longer and more awkward-to-read names.
Getters and setters
The Java standard library makes extensive use of the get
prefix for observer methods and the set
prefix for mutator methods. Unfortunately, many people inferred from this that classes should provide getters and setters for all of their fields, which defeats much of the purpose of encapsulation! To help break this association between methods and fields, we recommend that:
- Observers should omit a
get
prefix in most cases; they should simply be named after the abstract property they are observing. This keeps the name shorter without losing clarity. Note that the number of elements in a Java collection is queried bysize()
, notgetSize()
—this is a good example. It is okay for an observer to have the same name as a field. - For mutators, first ensure that you really want your type to be mutable (remember that immutable types have many advantages). If your type is mutable, prefer domain-specific verbs (like “rename” or “prepend”). In some situations (such as implementing the Builder pattern), setter methods named with a
set
prefix are justified. - If you are extending or complementing an existing Java class, it is okay to follow its convention for naming observers and mutators for the sake of consistency.