A critique of one section:
keep your function and method names as descriptive as
possible - try using upper and lower cases, avoid the
use of abbreviations if possible, the names should not
be very long or very short. And finally, be consistent
in following the naming scheme.
The length of a name should typically reflect the importance of that
omethod/function to the program as a whole. Short names can be perfectly
reasonable in the right situation -- for example, if you're putting in a
placeholder (e.g. a pure virtual function in a base class) for a
function that's going to be evaluated in numerical integration, it's
usually perfectly reasonable to just name it f() -- that's exactly what
you're going to see in a lot of text books.
Names should be long enough to make their function clear, but no longer.
keep your variable names as descriptive as possible - use
both upper and lower case, avoid too much abbreviations,
use as many characters as required to be sufficiently
descriptive. Finally, be consistent in following your
naming conventions.
Names should be as descriptive as necessary, NOT as descriptive as
possible. When you're going to iterate across an array, 'i' is a much
better name than 'array_index_variable'.
CamelCaseIsLessReadable_than_embedded_underscores.
The functions and methods should be smaller in size, less
than 100 lines of code is good to have, while less than 50
lines of code is preferable in most situations.
While it's reasonable to take a close look at whether longer methods can
be split up reasonably length by itself isn't a good indicator of
quality.
There should be well defined comments preceding a function
that describe the function and the functionality it provides.
Almost always wrong. If the name doesn't describe the function
sufficiently, it's probably poorly named and quite possibly poorly
defined as well.
The code should be well organized for readability.
Duh.
The use of whitespace should be generous both vertically
and horizontally
Rarely. Needing whitespace very much generally indicates that the code
has other problems.
One line of code should not exceed more than 70 characters.
First of all, that's ungrammatical. You mean: "One line of code should
not exceed 70 characters."
Second, it's almost complete nonsense. There are certainly a few of us
who remember 80-column Hollerith cards, but their day is long gone and
thankfully so.
One line should have one statement.
I suspect you meant "...have no more than one statement".
Still nonsense. Sometimes code is much clearer by putting more on a
single line. For one obvious example, consider a switch statement in C
or C++. The code can be much clearer by putting more on a single line:
switch (c) {
case '-': a -= b; break;
case '+': a += b; break;
case '*': a *= b; break;
case '/' a /= b; break;
}
At least IMO, putting each statement on its own line makes the code
_less_ readable:
switch (c) {
case '-':
a -= b;
break
case '+':
a += b;
break;
case '*':
a *= b;
break;
case '/':
a /= b;
break;
}
Your coding style should follow a pattern throughout the
program. The coding style includes stuff like using
indentation, brackets, naming conventions etc.
Consistency is _generally_ a good thing, but: "a foolish consistency is
the hobgoblin of small minds". Emerson's still right. Just for one
obvious example, it can make perfect sense to format code for a finite
state machine quite a bit differently than you would the same statements
in other code.
A common rule of thumb is that there should be as many
lines of comments in your code as many lines of code
are, so include as many comments as you can.
That rule of thumb _may_ be common, but it's _definitely_ lousy.
Comments should be sufficiently voluminous to do their job, no more and
no less. In some cases that means the comments outweigh the code by a
wide margin. In other cases, it means no comments at all.
IMO, quantity of comments matters far less than the quality and
(particularly) the subject matter. A comment should rarely be needed to
explain what the code does. What a function (for example) does should be
obvious from its name. Comments are necessary for things like _why_ the
code was written this particular way rather than another.
Bad example:
/* Sort: Sorts a set of account records. The sorting criteria is
* determined by the comparison function that is passed in the second
* parameter.
*/
void sort(Account_list accounts, compare *cmp);
Good Example:
/* Sort: We use an insertion sort because the accounts are always
* nearly sorted, and there are never more than 200 accounts anyway.
*/
void sort(Account_list accounts, compare *cmp);
The application should have a proper documentation
irrespective or how small or how big the application is.
The documentation should define process and flow of the
program, and even a few paragraphs would serve the
purpose, or if possible, we should focus on preparing a
separate flow chart and detailed program documentation.
Depending on how you define "proper", that's probably a tautology. OTOH,
implying for a moment that a throwaway utility needs the same
documentation as a product being shipped to end-users is nonsensical.
the exception and error handling processes should be
used as much as possible along with the status and error
logging mechanisms.
"As much as necessary", not "as much as possible". Since you mention C++
a lot, I'll use its syntax for an example:
try {
something();
}
catch(int) {
throw;
}
This is foolish. It's entirely possible, but pointless and stupid.
In C++, in order to reduce the complexity to the maximum
and increase maintainability to maximum, avoid a lot of
inheritance mechanism usage (these are unavoidable if
the application actually requires them). Multiple
inheritance should be reduced to maximum, and operator
overloading should also be avoided.
Replace "maximum" with "minimum" and that paragraph at least starts to
make a little sense. No application truly _requires_ inheritance. Trying
to create any small rule like this about when it's appropriate to user
inheritance or not is almost certainly doomed to failure. This is really
a judgment call, and any attempt at a rule of thumb is likely to do more
harm than good.
Operator overloading should be embraced to the extent that it makes
sense, no more and no less. Avoiding it when it make sense is downright
foolish. When it makes sense, operator overloading can improve
readability considerably. Just for an obvious example compare:
a = b * c + d;
to:
a.assign(b.mult(c).add(d));
Clearly operator overloading improves readability considerably in this
case. Of course, it's possible to do stupid things with operator
overloading, but an overloaded operator is nothing more or less than a
specific name for a function. Avoiding it because it can be abused is no
more sensible than avoiding the use of functions because some idiot
could name a function 'add' even though it really does subtraction.
In C++, the class methods should be kept small, less than
50 lines of code is usually preferable.
See my previous comment on function length. Being a class method has no
real effect on the length of a function -- it should be as long as
necessary to do the job, no more and no less. A method for (for one
example) deleting a node from a balanced (e.g. AVL or red-black) tree
will almost always be longer than 50 lines. Trying to split it up into
"chunks" of 50 lines or less will make it _far_ less readable than
writing it as a single, coherent function that happens to be around 100-
150 lines.
In C++, exception handlers should be extensively used.
Except when they shouldn't. See my previous comment on exception
handling. Exception handling should be treated as the preferred error-
handling mechanism as a general rule. Exactly how extensive that is will
depend _heavily_ on the kind of code being written. Exceptions are often
generated close to the bottom layer of the code, and handled close to
the top layer. Well-written code for the intermediate layers often needs
no exception handlers at all -- it just needs to provide a specified
guarantee in the face of exceptions. This will often involve techniques
such as RAII and no exception handlers at all -- in fact, if you catch
yourself (no pun intended) having to write exception handlers for such
intermediate code, chances are that using them to cover for other
problems in the code.
--
Later,
Jerry.
The universe is a figment of its own imagination.
[ ... ]
> switch (c) {
> case '-': a -= b; break;
> case '+': a += b; break;
> case '*': a *= b; break;
> case '/' a /= b; break;
Oops -- sorry for the typo. That, of course needs a colon:
case '/': a /= b; break;
--
> keep your variable names as descriptive as possible - use
> both upper and lower case, avoid too much abbreviations,
> use as many characters as required to be sufficiently
> descriptive. Finally, be consistent in following your
> naming conventions.
>
> Names should be as descriptive as necessary, NOT as descriptive as
> possible. When you're going to iterate across an array, 'i' is a much
> better name than 'array_index_variable'.
>
> CamelCaseIsLessReadable_than_embedded_underscores.
In addition, in at least one setting, CamelCase is totally unusable.
I was maintaining a significant piece of VHDL code that used
CamelCase heavily, but the simulation tool
displayed all symbol names (not actually sure if "symbol
name" is correct terminology in VHDL, but you get the point)
in lowercase, making all variable names totally unreadable.
Perhaps simulation tools are available now that maintain
case.
In short: CamelCase == evil.
>> CamelCaseIsLessReadable_than_embedded_underscores.
>
> In addition, in at least one setting, CamelCase is totally unusable.
> I was maintaining a significant piece of VHDL code that used
> CamelCase heavily, but the simulation tool
> displayed all symbol names (not actually sure if "symbol
> name" is correct terminology in VHDL, but you get the point)
> in lowercase, making all variable names totally unreadable.
> Perhaps simulation tools are available now that maintain
> case.
>
> In short: CamelCase == evil.
The Ruby on Rails community likes under_bars for a simple reason: The closer to
grammatically correct English, the better. For example:
Order.has_many :line_items
That bonds the Order model (fronting the 'orders' table in the database) to the
LineItem model (fronting the 'line_items' table).
After you get over the initial shock that the model name isn't the same
plurality as the database table, then the shock that sometimes it is, you never
look back, and you generally always get the pluralities right.
--
Phlip
> The Ruby on Rails community likes under_bars for a simple reason: The
> closer to grammatically correct English, the better. For example:
>
> Order.has_many :line_items
There is actually another reason. Back in the '70s two studies were done
on the affect of case on reading comprehension in software programs. As
I recall one measured eye movements as someone read a program and the
other measured errors detected in code reviews. Both independently
concluded that mixed case identifiers reduced readability, masked errors
in reviews, and led to the insertion of defects compared to using
underscores.
However, Nicolas Wirth had great influence at the time and he hated
underscores (they were not allowed in Modula 2 syntax). The C community
then adopted the PASCAL/Modula convention of using mixed case
identifiers and we have been paying the price ever since. (There was
also some backlash because back then a lot of COBOL and FORTRAN was
written with underscores and none of the June Grads wanted to be
associated with anything as pedestrian as COBOL.)
Alas, I lost the hardcopies in a move decades ago and have not been able
to find the references again, despite a lot of looking over the years.
(The electronic technical databases only date back to ~1984, which makes
it tough to find things before that time.) If anyone happens to have
them, I would really appreciate it if I could recover them.
*************
There is nothing wrong with me that could
not be cured by a capful of Drano.
H. S. Lahman
h...@pathfindermda.com
Pathfinder Solutions
http://www.pathfindermda.com
blog: http://pathfinderpeople.blogs.com/hslahman
"Model-Based Translation: The Next Step in Agile Development". Email
in...@pathfindermda.com for your copy.
Pathfinder is hiring:
http://www.pathfindermda.com/about_us/careers_pos3.php.
(888)OOA-PATH
Good commenting is very important.
Flow diagrams too help.
Any complex procedures should be documented.
> It doesnt reall matter what functions names are if you come back in 6
> months time and have no idea what anything is doing.
That's why you should write wall-to-wall unit tests. If you leave code for 6
months, you can come back to it and start working again. To the extent that the
tests prevent you from making mistakes, you will resume your earlier velocity.
> Good commenting is very important.
Test cases should be clear and literate. And you should treat code comments with
suspicion. Many represent a missed opportunity to improve code's readability.
The simplest example:
int x = 42; // number of fish
The improvement:
int number_of_fish = 42;
This effect helps readability, at all scales.
> Flow diagrams too help.
Test cases should also illustrate control flow.
> Any complex procedures should be documented.
Given a choice between spending an hour writing external documentation, and
spending an hour improving tests, engineers should nearly always choose the
latter. Documentation can lie easier than tests can. And documenting _with_
tests will kill multiple birds with one stone.
--
Phlip
However, with a good naming convention, it
will take far less time to figure out what things
are doing. Consider:
at call point:
xxyyzz1( x, j );
at definition:
/*
* xxyyzz1 computes a power: base ^ exponent
*/
long xxyyzz1( long base, long exponent )...
Compared to the following at the call point:
power( base, exponent );
In the latter, you don't even need to look
at the definition of the function to know
what it does.
Names are **extremely** important.
Fish; scales; I get it!
-paul-
Well, there's two schools of thought there...