This page contains a list of recommended "best
practices" and "idioms" that deal, for the most part, with program
development and coding activities using Java or C++. Some of the items
are language-independent and some pertain more directly to one or the
other of these two languages.
For Java or C++ (and probably lots of other languages)
- Pay attention to detail. Remember that "Carelessness
Costs!". This can be a life-and-death thing in certain situations, as
we all know. In a programming context, no one should have to remind
you not to be careless with your code, so we won't. But there is
another area in which students, unfortunately, need continual
reminding, and it's this: documentation. Documentation should be
given the same care and attention as every other aspect of your
program development. This means more than just having complete and
correct information in your documentation. In addition, it means
paying attention to English grammar, spelling, punctuation,
subject/verb agreement, proper use of singular and plural, and the
like. In an academic context, carelessness in this area can simply
cost you points on a homework submission. In the real world, it can
give those reading what you have written a lack of confidence in your
abilities. The implicit question that may be in the mind of the
viewer is this one: If this person has not paid attention to those
details, what other details have also been ignored? [Google "Van Halen
and brown m & m's" for an interesting, and related, story.]
- Use a single Test project. When using and IDE (Integrated
Development Environment) such as NetBeans, Eclipse, JCreator, or Visual
Studio for testing sample programs that have been supplied by your
instructor, or for developing short sample programs of your own, it's
simpler and more convenient to use a single project called "Test"
(for example). You can then move the individual sample programs in
and out of this one project as you test them. This is "better" than
creating a separate project for each sample program, which will
quickly lead to serious "clutter" in your account. You can also follow
this general principle even if you are just using and editor and
compiling at the command line.
- Make sure your source code contains no TAB characters.
Source code that contains TAB characters may print differently on
different printers, or may display differently when viewed in a
console window from how it displays when viewed in an IDE editor
(NetBeans or Visual Studio, for example). In particular, it may not
come out on a hard copy formatted as you had intended, or as it looked
on the screen. Any of these situations may arise when you have TAB
characters in your source code files. Each such situation will
probably make your code much harder to read, and may well cause you
lost points.
- Make sure your source code lines are not too long.
What does this mean? For our purposes it means this: Source code that
will be evaluated in any way will be either printed out, or observed
on a screen that may not be wider than 80 characters, and you should
therefore make sure your that your source code lines are all strictly
less than 80 characters long (a maximum of 72 characters is
recommended). Failure to do this will likely mean that your code will
contain "wrapped" lines, making it much harder to read, and thus costing
you lost points.
- Submit early and often. When you are developing an
assignment to be handed in via the Web, it is a good idea to "submit
in stages". That is, you should be developing your solution in such a
way that, at several times during the development, you are a stage
where your program is working, exhibits a good programming style, and
the working parts are fully documented. If so, submit it at that
stage. Why? Well, doing so will help guide your development process
and get you familiar with the submission procedure, for one thing.
For another, should—God forbid—something go wrong, or
should you run out of time at the end and not be able to submit your
final version, you would have at least something already
submitted.
- Terminate most console programs with a pause. Make
sure that most console programs you write that are designed to
interact directly with a human user have a "pause" at the very end,
so that if such a program is run by double clicking on it in Windows,
the console window that opens to run it will have the pause at the
end. This will avoid the potential problem of the user "missing" some
or all of the output from your program when that console window
closes abruptly as your program ends. When testing programs
within an IDE that supplies a pause automatically after a program
finishes running, this point is often overlooked. On the other hand,
if the console program is designed to be simply a "utility" program
that takes some input from a file or the command line and writes some
output to a file (for example) this "best practice" may not need to
be followed.
- Make sure any textfile has a newline character at its
end. Some operating systems, or at least some editors on some
operating systems, make sure that there is a newline character at the
end of a textfile when the file is saved. This is an excellent idea,
and simplifies the processing of such files. So, as a "best
practice", it is a good idea to make sure that any text file being
processed by one of your programs has a newline character at the
end. One way to do this is to load the file into an editor and move
the cursor to the end of the file, at which point the cursor should
be on a line by itself, and at the beginning of that line. The
alternative, of course, is to write more complicated code to deal
with the possibility that there might not be a newline character at
the end of a file that you have to process.
- Use "setters" and "getters" in your classes. For class
member functions that either set or get the value of a data member
(i.e. that either assign a value to, or return a value from, a class
data member), use a consistent naming scheme that begins with "set"
or "get", as the case may be, and is followed by the name of the
thing being set. For example, in a Time class you might have
both a setHours() member function and a getHours()
member function. This convention is consistent with widespread
current practice, and is independent of any other naming or
capitalization conventions that may be in place.
- Format prompts properly. If a console program prompts
the user for input which is to be entered on the same line as the
prompt, ensure there is a blank space between the end of the prompt
and the start of the entered input. Such a prompt should normally end
with a colon, or possibly a question mark, depending on the nature of
the prompt.
-
Make use of canonical input/output algorithms. It is
almost always a good idea to use input and output algorithms that
implement the following pseudocode:
For input
---------
Open the stream from which input data is to come
If unsuccessful report the problem and return (or exit)
Try to read the first input data item from the input stream
while successful
Process the data item
Try to read another data item
endwhile
Close/clear the input stream
For output
----------
Open the stream to which output data is to go
If unsuccessful report the problem and return (or exit)
while there is more output data
Write next data item to the output stream
endwhile
Close/clear the output stream
Note that if the input stream is standard input and/or the output
stream is standard output, the opening and/or closing of the stream
may not be necessary or desirable. Furthermore, the pseudocode does
not include any error checking, which is always a good idea and
should normally be part of the actual code.
- Position variable declarations to enhance readability
When declaring a variable, place its declaration as close as possible
to where the variable is used, particularly if the variable is only
used once. Variables used many times may have their declarations
grouped and placed as near as possible to the first use of any
variable from that group. Whenever possible, declare a for-loop
control variable in the for-loop itself, which limits the scope of
the variable to the loop in which it is declared.
-
Clear an input stream after reading from it. This is
just good programming practice, after all. But it also means that
your program will not suffer from hard-to-track-down bugs caused by
a subsequent read that is actually reading one or more "left-over"
characters from previous input, rather than what you think it is
reading. Failing to do this is one of the most frequent causes of
the "bug" that shows up when you try to make the output from your
program "pause" for the user to read it, and it does not pause when
it should. This advice applies whether you are reading input from
standard input or from a textfile.
For Java
- Put a main() in every class Get into the habit of writing
a main() test driver for each of your classes. This allows you to test
your class "in isolation" and make sure it's working properly before
including it within a larger context.
-
Close any file when your program has finished reading from it
or writing to it Though the operating system may do this for
you when your program ends, you should not rely on this happening,
and you may run into trouble if you neglect to do so and then try
to use the same file (or the same file variable) for another purpose
later in your program.
- Understand jar files Make sure you know how to create
executable jar files to "package" your programs into a single
executable file. [Note: The term "package" as used here does not
refer to the Java
package
statement.]
- More to come ...
For C++
-
Give your source code files informative extensions Use
the following extensions for your source code files:
- .cpp for a file that contains a main function, as
well as for other files that are going to be compiled separately
and linked with the one containing the main function
- .h for a header file which is going to be included
in one or more .cpp files, and whose content forms just
the specification of one or more classes, and/or one or
more free-function prototypes (so, in this case no, or not many,
actual function definitions, and those that are present would be
short inline functions in a class definition, for the most part)
(These are header files that would generally require a
corresponding implementation file.)
- .hpp for a header file which is going to be included
in one or more .cpp files, and that contains the
complete specification and implementation code for one
or more classes and/or one or more free functions
-
Use automatic string concatenation. When sending
multiple lines to the standard output with a C++ cout
statement, use "automatic string concatenation" combined with one
or more trial runs to find the position(s) within the text where \n
needs to be inserted to produce line breaks. So, for example to
output the previous sentence, begin by writing
cout << "When sending multiple lines to the standard output "
"with the C++ cout statement, use \"automatic string "
"concatenation\" combined with one or more trial runs "
"to find the position(s) within the text where \\n needs "
"to be inserted to produce line breaks.";
which makes use of "automatic string concatenation". (That is, you
do not need to place an insertion operator << in front of the
second to fifth lines in the above code segment.) Then compile,
link and run the program containing this code, observe and note
where the displayed lines "wrap around", and then revise the code
so that it contains \n in the appropriate places to produce a
sufficient number of line breaks in the output to give a pleasing
display.
-
Close a file when finished with it and clear a file after
closing it. It is a good idea to "clear" a file variable
after closing it. Much of the time this may be unnecessary, since
(for example) the operating system may close a file for you after
your program exits. But, for those times when it may be very
necessary to do so (like the case where you want to use the file
variable again later in the same program, perhaps), getting into
the habit will save you some agony and possibly some embarrassment.
So, think of this as a potentially face-saving "idiom":
someFile.close(); someFile.clear();
Note that the two statements are shown written on the same line.
Normally it is a better idea to have one statement per line in your
source code. However, in a case like this where the two statements
are "intimately connected" and form an "idiom", writing them both
on the same line (since both are very short as well) emphasizes the
connection between them, and hence may be excused (if an excuse is
necessary) on those grounds.
- Consider writing error messages to cerr, not to cout.
This may be a good idea for the simple reason that cout is
more likely to be "redirected" (to a file, say), which may cause the
user to miss an important error message. Writing errors to
cerr is a better way to ensure that they appear on the
user's console, where they are more likely to be observed in a timely
manner, even if after the program has actually run. On the other
hand, if the program is designed to be used exclusively in
interactive mode with the user at a console entering input and
observing ouput as the program is running, writing errors to
cout is fine. The bottom line is to make sure the user does
not "miss" anything that the user should see, whatever may be the
destination of any part of the output.
- Explicitly include any required header files. You
might think you're required to do this anyway. But no. For example,
many C++ programmers are accustomed to rely upon the inclusion of
<iostream> to provide facilities from the
ios, istream, and ostream classes. But the
C++ Standard does not require this, and in any case it is just good
programming practice to include explicitly each header file you need,
even if it might be automatically included or included by another
header you are using. This is one of the more difficult "best
practices" to follow rigorously, for the simple reason that it takes
a bit of effort to find out exactly where things are located in the
class hierarchy and which header files include what. But this effort
can also be good learning experiencej, and it will most certainly
make your code more portable.
-
Protect your own header files from "double inclusion"
Whenever you write a .h or .hpp header file that
may be included in one or more other files in your project, be sure
to protect it from "double inclusion" with the following three
lines:
#ifndef FILENAME_H
#define FILENAME_H
...file contents except for initial comment here...
#endif
-
Use the idiom for processing C-strings. The simplest way
to process every character in a C-string s is to use the
idiom suggested by
for (int i=0; s[i]; i++) Process(s[i]);
since this loop will terminate when s[i] is the
'\0' (null) character, as it eventually must become, in a
C-string.
-
Use the "get a value and move along" idiom where
appropriate. This is the idiom that returns a value in an
array or a container and then moves to the next value in that array
or container, as in these examples:
cout << a[i++];
cout << *p++;
In the first line, the value of the array a at index
i is displayed, after which the index i is
incremented. In the second line, the pointer (or iterator)
p is dereferenced and the resulting value displayed, after
which the pointer (or iterator) p is incremented. Note
that, for this idiom to work, the postfix form of the increment
operator ++ is required. A similar idiom applies, of
course, to analogous expressions involving the postfix decrement
operator --.