13.5 Avoiding errors by coding conventions


13.5 Avoiding errors by coding conventions

A programming convention is a set of choices. An individual or group of programmers decides to code in a certain style to achieve their goals. Programming conventions can make bugs less likely. They also can make it easier to diagnose problems when they do occur and easier to modify them once the problems are found.

We prefer the term “programming convention” to “programming standard.” Programming standards tend to be imposed on programmers by managers. Programming conventions tend to be agreements among peers that are largely self-enforcing.

Programming conventions aren’t the Ten Commandments. They are selected by people, rather than dictated by God. There is no justification for breaking a divine commandment. There may be, however, occasions when it’s appropriate to ignore a tenet of a programming convention to achieve some other purpose. Most programming standards or conventions presented in the literature tend to be a bit arbitrary or idiosyncratic.

There is rarely only one reasonable choice for any given coding style issue. While our suggestions are carefully thought out, you could achieve similar results by a consistent set of different choices. Almost any programming convention is better than no convention at all. Consistency rewards its practitioners.

How does consistent coding reduce the number of bugs in an application? Consistency enables the programmer to code with less mental effort. If a given construct is always written the same way, the programmer will learn to do so without conscious thought. Since every conscious effort presents the chance for making an error, making coding a mechanical task reduces errors. It also makes it possible to automate certain programming tasks.

How does consistent coding make it easier to diagnose problems when they do occur? Consistency enables the programmer to understand the code more quickly. For example, if a programmer sees a name written in a certain way, he or she will be able to infer certain information about the object to which it refers. He can do this without having to look at its definition, which is likely to be on a different screen or page of the program source.

How does consistent coding make it easier to correct erroneous code? Consistency enables the programmer to identify all sections of code related to the change he or she wants to make. If a given construct is always written in the same way, the programmer can have confidence that if these sections are changed, nothing will have been omitted.

The purpose of this section is to present a general approach to programming conventions. This approach applies to a variety of programming languages, including those that aren’t “O-O kosher.” The organizing concept for our approach to programming conventions is object-oriented programming. The phrase “object-oriented” has been applied to every computing activity imaginable. This overuse shouldn’t cause us to lose sight of an important concept.

13.5.1 Object-oriented programming

You can think of object-oriented programming as designing and programming an application-specific computer architecture. Such architectures are often referred to as “virtual machines.” A computer architecture has a set of primitive data types, such as integers and floating-point numbers. The operations are defined on the primitive data types, such as addition and comparison. Anything the computer can do must ultimately be defined in terms of these types and operations. A virtual machine has the same features as a real machine. The difference is that the virtual machine’s features are defined in software.

Object-oriented programming involves three steps:

  1. Find those data types and operations that are fundamental to a given application.

  2. Implement those types and operations.

  3. Code the application in terms of those types and operations.

The application only refers to the data structures that implement the objects through the procedures that implement the fundamental operations. The application need not know how the basic data types are implemented. Access to the data structures is made only through the interface provided. The implementation of an object can be completely replaced without harming the application.

Our approach to programming conventions structures them into the following sections:

  • The structure and format of source files

  • The structure and format of classes

  • The structure and format of procedures

  • The structure and format of statements

  • Consistent construction of names

  • Constructs that should be avoided

13.5.2 Programming convention

Each of the sections of the convention consists of a set of metarules for deriving specific standards and a commentary on the metarules.

13.5.2.1 Source-file metarules

  • Recommend a consistent file-naming convention.

  • Recommend a helpful line-length limit.

  • Recommend a block of comments that describe the most important aspects of a class before the definition of the class.

  • Recommend a block of comments that describe the most important aspects of a procedure before the definition of the procedure.

  • Avoid mandating comments that can become easily outdated or that are readily discerned from reading the text.

  • Recommend a method for distinguishing the beginning of new class definitions and new procedure definitions.

13.5.2.2 Source-file metarules commentary

Both programming languages and platforms impose constraints on the names that programmers can give to source files. Java requires that the name of a file correspond in spelling and capitalization with the public class that it contains. Back in the bad old days, many operating systems restricted the length of file names, often to eight characters. Fortran 77 restricted the length of procedure names to six characters.

The most common line-length limits correspond to the form factors of video displays and printer paper. Don’t assume that everyone who looks at your program will have a wide display or wide paper.

The most useful information about a procedure is a one-sentence description of its purpose. References to published literature, such as where the algorithm comes from, are also quite useful.

Code changes, so why encourage the comments to get out of date by putting information in comments that is readily obtained from the source as well? This includes the name of the procedure, the names and types of the arguments, and the type of the return values.

Delineating the definitions of procedures and objects makes it easier for a reader to scan your source code looking for items of interest. White space or comments containing a separating line are the most common ways to achieve this goal.

13.5.2.3 Source-file convention examples

Listed as follows is a set of conventions for C++ that address the issues of file structure. There are other choices that are reasonable and effective in preventing bugs.

  • Do name include files with an extension of “H”.

  • Do name implementation files with an extension of “C”.

  • Do name the include file for a class the same as the name of the class.

  • Do use upper- and lowercase letters in file names as they’re used for class names in source code.

  • Do use “//” to denote comments. Don’t use “/* */” to denote comments.

  • Do write comments at the beginning of an include file that describe the purpose of the class defined.

  • Do write comments before a function definition that describes the purpose of the function defined.

  • Do put all data members, member functions, and friend functions with each other in the source file. Separate each group with the following comment:

     //====================================================== 

  • Do separate each function with the following comment:

     //------------------------------------------------------

  • Do provide all include files with some means of preventing multiple inclusion of that file.

  • Do break source lines of more than eighty characters over multiple lines.

  • Don’t put more than one class definition in an include file.

  • Don’t define functions in the class header file.

  • Don’t put names of formal arguments in function definitions in header files.

Listed as follows is a set of conventions for Java that address the issues of file structure. There are other choices that are reasonable and effective in preventing bugs.

  • If a nonpublic class supports only one public class, put it in the same file as the public class; otherwise, put it in a separate file.

  • Do use javadoc conventions to describe the purpose of each class.

  • Do use javadoc conventions to describe the purpose and contents of instance and class (static) variables.

  • Do use javadoc conventions to describe the purpose, assumptions, and effects of methods.

  • Do use “/* */” for block comments.

  • Do use “//” for inline comments.

  • Do put two blank lines between major sections of a source file.

  • Do put one blank line between method definitions.

  • Do put one blank line between the local variables of a block and the first statement.

  • Do put one blank line before a block or single-line comments.

  • Do put one block line between logical sections of a method.

13.5.2.4 Class structure metarules

  • Recommend ways to hide the implementation of procedures and the internal representation of data from the user of a class.

  • Recommend ways to provide a full set of operations for each data type to be defined.

  • Recommend ways to employ polymorphism to achieve maximum code reuse.

  • Recommend ways to use inheritance to share the common features between classes.

13.5.2.5 Class structure metarules commentary

Data hiding isn’t object-oriented programming per se. Some authors refer to encapsulation and data abstraction as object-based programming. They distinguish this from object-oriented programming, by which they mean the use of inheritance and polymorphism.

13.5.2.6 Class structure convention examples

Listed as follows is a set of conventions for C++ that address the issues of class structure. There are other choices that are reasonable and effective in preventing bugs.

  • Do encapsulate global variables, global constants, and enumerations in a separate class.

  • Do declare public, protected, and private sections of a class in that order.

  • Don’t create public or protected data members in a class.

  • Do declare as inline all accessor member functions.

  • Do declare as inline all mutator member functions. Mutators assign their argument to a data member.

  • Don’t declare as inline constructors or destructors.

  • Do declare as const a member function that doesn’t change the values of data members.

  • Do define a virtual destructor for classes used as base classes that have virtual functions.

  • Do define a copy constructor for classes that contain pointer or reference data members.

  • Do define an assignment operator for classes that contain pointer or data reference members.

  • Do define assignment operators to return a const reference to the assigning object.

  • Don’t return non-const references or pointers to data members from public member functions.

  • Do give derived classes access to data members by providing protected accessor functions.

  • Do use the Standard Template Library wherever possible.

Listed as follows is a set of conventions for Java that address the issues of class structure. There are other choices that are reasonable and effective in preventing bugs.

  • Do put the items in the definition of a class in the following order:

    • Class (static) variables: public, protected, private

    • Instance variables: public, protected, private

    • Constructors

    • Other methods

  • Do declare each class and instance variable on a separate line.

  • Don’t make instance variables public.

  • Don’t make class (static) variables public unless they’re constants.

  • Do access class (static) variables or methods through the class name, not the name of an instance.

  • Do initialize class (static) and instance variables in the constructor.

  • Do use the Java Collections Framework classes wherever possible.

13.5.2.7 Procedure metarules

  • Recommend a consistent method for listing and aligning the return types and argument lists of procedures.

  • Recommend using language features to ensure type-safe procedure linkage.

  • Recommend a consistent method for locating the placement of local name definitions within a procedure.

  • Recommend a procedure length limit based on the semantic complexity of the procedure.

  • Recommend a control structure nesting limit based on the semantic complexity of the statements.

  • Recommend inserting blank lines to group related lines of text.

13.5.2.8 Procedure metarules commentary

Defining local variables where they’re first assigned or initialized reduces the total number of lines of code. It also reduces the need to look back up the page for declarations. Some people prefer to find all local variable definitions in one place. Consistency is the key here.

The cyclomatic complexity of a procedure describes the control-flow graph that can be induced from that procedure. For most programming languages, it can be computed by adding one to the number of control-flow decisions. People have difficulty understanding procedures with greater complexity. The depth of nesting of control structures is another measure of the complexity of a procedure.

13.5.2.9 Procedure convention examples

Listed as follows is a set of conventions for C++ that address the issues of procedure structure. There are other choices that are reasonable and effective in preventing bugs.

  • Do specify a return type for all functions.

  • Do put the return type of a function on a separate line from the name of the function.

  • If a function has one argument, put it on the same line as the function name.

  • If a function has more than one argument, put the argument list on the line following the function name.

  • Don’t use unspecified function arguments with ellipsis notation.

  • Do declare local variables as near to their first assignment as possible.

  • Do declare all local variables in separate statements.

  • Do assign a value to local variables where they’re declared.

  • Do prefer initializing local variables over assigning values to them.

  • Do insert blank lines to group logically related statements.

Listed as follows is a set of conventions for Java that address the issues of procedure structure. There are other choices that are reasonable and effective in preventing bugs.

  • Do put the return type on the same line as the method name.

  • Do put the argument list on a line following the method name.

  • Do put the parenthesis immediately after the method name.

  • Do declare each local variable on a separate line.

  • Do initialize variables where they’re declared.

  • Do declare variables at the beginning of the block that delimits their scope.

  • Do insert blank lines to group logically related statements.

13.5.2.10 Statement metarules

  • Recommend indenting and aligning nested control structures.

  • Recommend safe handling of default cases for multiway branches.

  • Recommend testing loops at the top whenever possible.

  • Recommend starting each statement on a new line.

  • Recommend writing no more than one simple statement per line.

  • Recommend a method for breaking compound statements over multiple lines.

  • Recommend an approach for placing blank spaces within a statement.

  • Recommend an approach for using parentheses in expressions where operators have different precedence.

13.5.2.11 Statement metarules commentary

Indenting is a powerful way to show someone what control statements control other statements, if it’s used consistently. Placing blank lines between groups of related statements allows the reader to “chunk” blocks of code and aids reading and comprehension.

Providing a default case for multiway branches and testing a loop at the top both make a procedure more robust. The former handles cases not explicitly listed, while the latter handles the case in which the loop shouldn’t be executed.

Putting more than one statement on a line can make it difficult to stop and step in some source-level debuggers. Blank spaces are most helpful to readers when they separate groups of related characters, like a word, rather than separating individual characters. Believe it or not, the use of blank spaces between words did not occur for over a thousand years after the Phoenicians invented the alphabet.

If you’re writing expressions so complicated that a few extra parentheses makes the expression spill over a line, you’re probably writing expressions that should be broken into pieces. C statements with several predecrement and postincrement operators may be cute but are no longer necessary to cause a C compiler to generate efficient code. Compilers have advanced quite a bit in two decades.

13.5.2.12 Statement convention examples

Listed as follows is a set of conventions for C++ that address the issues of statement structure. There are other choices that are reasonable and effective in preventing bugs.

  • Do indent four spaces for nested control-flow constructs.

  • Do indent four spaces for nested class members.

  • Do split lines that run over eighty characters:

    • Break after a comma.

    • Break before an operator.

    • Indent eight spaces from the left edge of the beginning of the statement.

  • Do put opening braces on the same line as the keyword they’re associated with.

  • Do put closing braces on a separate line, indented to the same level as the associate keyword.

  • Do follow the if, else, while, for, and do keywords with a compound statement block, rather than a simple statement, even if the block contains only one or even zero statements.

  • Don’t put spaces around the reference operators “.” and “->” or between unary operators and their operands.

  • Do put spaces between binary operators and their operands.

  • Do conclude the code after a case statement with a break statement.

  • Do include a default case in a switch statement.

  • Avoid using do while loops.

  • Do put each simple statement on a separate line.

  • Don’t assign more than one variable in a statement.

  • Do parenthesize expressions with more than one operator, so that the default precedence rules are made plain.

Listed as follows is a set of conventions for Java that address the issues of statement structure. There are other choices that are reasonable and effective in preventing bugs.

  • Do split lines that run over eighty characters:

    • Break after a comma.

    • Break before an operator.

    • Break after a parenthesis.

  • Do indent one tab stop for nested control constructs.

  • Do indent one tab stop for nested class members.

  • Do put an open brace on the same line as the declaration statement.

  • Do put a closing brace on a separate line, indented to the same level as the first keyword of the declaration statement.

  • Do put each simple statement on a separate line.

  • Don’t put extra parentheses in a return statement.

  • Do indent statements in a compound statement one level.

  • Do put braces around the statements of a compound statement, even if there is only one statement in the block.

  • Do put an open brace on the same line as a control-flow statement.

  • Do put a closing brace on a separate line, indented to the same level as the first keyword of the control-flow statement.

  • Do put case statements at the same level of indentation as the switch statement that controls them.

  • Do indent the statements of a case one level.

  • Either put a break statement or a comment that the case falls through at the end of every case.

  • Don’t assign more than one variable in an assignment statement.

  • Do parenthesize the first operand of the ternary conditional operator.

  • Do put a blank space after keywords that are followed by parentheses (while, if, for, switch).

  • Do put a blank space after commas in argument lists.

  • Do put a blank space between binary operators and their operands, except for the dot operator.

  • Do put a blank space after semicolons in compound statements.

  • Do put a blank space after cast operators.

  • Avoid using do while loops.

13.5.2.13 Naming-convention issues

The following issues must be dealt with when considering a convention for the selection of names for program entities:

  • How should nonalphabetic characters be used?

  • How should upper- and lowercase letters be used?

  • How should compound names be formed?

  • How should acronyms and abbreviations be used?

  • Under what circumstances should names be reused?

13.5.2.14 Naming-convention metarules

General issues
  • Recommend choosing names that are meaningful in the application context.

  • Recommend choosing names that suggest their use in the program.

  • Recommend spelling names according to the rules for minimizing mistyping or misreading names.

Glyph choice
  • Recommend using special characters in names only to convey extra information to a person reading your code.

  • Recommend using upper- and lowercase letters to form names that help a person understand your code more quickly.

Compound names
  • A compound name is formed by chaining several words from a natural language together to form a name.

  • Recommend using noun phrases to create names for data, such as variables or files.

  • Form noun phrases from adjectives and nouns.

  • Recommend using verb phrases to create names for procedures.

  • Form verb phrases by beginning with an action verb that modifies an object noun.

  • Recommend forming compound names so they’re easy to read and understand.

Abbreviations and acronyms
  • Recommend consistent abbreviations.

  • Recommend using one way to abbreviate a word in all contexts.

  • Don’t abbreviate a word in some places and spell it out in others.

  • Recommend condensing instead of truncating abbreviations.

  • Recommend condensing words by removing duplicate letters and removing easy-to-infer sounds.

Name reuse
  • Don’t use the same name for distinct objects in the same procedure.

13.5.2.15 Naming-convention metarules commentary

Some languages allow one or more nonalphabetic characters in user names. The underscore character is commonly used to separate words in a compound name, particularly if the name is written in all capital letters. The downside of this practice is that it makes names longer.

Some languages make no distinction between upper- and lowercase letters. Other languages do treat upper- and lowercase letters as distinct. Use upper- and lowercase letters to form names that help a person understand your code more quickly. A common use is to capitalize the first letter of each word in a compound name. Words composed of multiple adjacent capital letters are harder to read. Use capitals sparingly.

The human mind is adept at filling in missing vowels when a name is condensed by removing them. There are languages, such as Hebrew, that are written without vowels, and readers have no problem supplying them mentally.

The problem with acronyms is knowing the context in which to interpret them. CIA could mean Central Intelligence Agency or Culinary Institute of America. If you use acronyms, provide a dictionary.

Some languages, like C, allow programmers to create distinct instances of variables with the same name by using a block-structured scope. Weakly typed languages like APL allow different variables to be assigned to the same name in a single function.

13.5.2.16 Naming-convention examples

Listed as follows is a set of conventions for C++ that address the issues of name selection. There are other choices that are reasonable and effective in preventing bugs.

  • Do begin the names of classes, structures, typedefs, and enumerations with an uppercase letter.

  • Do begin the names of functions, variables, and constants with a lowercase letter.

  • Don’t begin names with an underscore.

  • Do name variables with nouns or noun phrases that suggest the data that they contain.

  • Do name functions with verbs or verb phrases that suggest the process they perform.

  • Do use an uppercase letter for the first letter of each word after the first word in a multiword name.

  • Do name accessor functions with the prefix get and mutator functions with the prefix set.

  • Do name functions that return a Boolean return with the prefix is.

Listed as follows is a set of conventions for Java that address the issues of name selection. There are other choices that are reasonable and effective in preventing bugs.

  • Do name packages with all lowercase letters. The uniquely identify ing prefix should begin with com, edu, gov, mil, net, org, or one of the two-letter country codes.

  • Do name classes and interfaces with nouns or noun phrases. The first letter of each internal word should be capitalized, including the first letter.

  • Do name methods with verbs or verb phrases. The first letter of each word (except the first, which should be in lowercase) should be capitalized.

  • Do name variables with nouns or noun phrases. The first letter of each word (except the first, which should be in lowercase) should be capitalized.

  • Do name constants with all uppercase letters. Each word in a phrase should be separated with an underscore.

  • Do name accessor functions with the prefix get and mutator functions with the prefix set.

  • Do name functions that return a Boolean return with the prefix is.

13.5.2.17 Language-construct avoidance metarules

  • Recommend avoiding language features whose implementation is platform dependent.

  • Recommend avoiding language features that will be removed from a future standard.

  • Recommend avoiding language features that have been superceded by constructs that are less error prone.

13.5.2.18 Language-construct avoidance metarules commentary

In almost every language, there are a few ill-considered constructs you should avoid. Most programming languages evolve. While language evolution is incremental, new ideas sometimes make old approaches obsolete.

Modern programming languages have few uses for the goto statement. C and C++ don’t have any means to exit from inner loops other than a goto. If you need to implement high-level control structures, such as finitestate machines or decision tables, you will need to code them using a goto.

Some constructs should be avoided because they’re proven error generators.

13.5.2.19 Language-construct avoidance examples

Listed as follows is a set of conventions for C++ that address the issues of construct avoidance. There are other choices that are reasonable and effective in preventing bugs.

  • Don’t use goto except to break out of loops that are multiply nested.

  • Don’t use malloc, realloc, or free.

  • Don’t assume that an int is thirty-two bits wide.

  • Don’t assume that pointers and integers are the same width.

  • Do use inline functions instead of #define with arguments.

  • Do use enumerations instead of #define for groups of named constants.

  • Do use const instead of #define for individual named constants.

  • Do use typedef to simplify declaration of function pointers.

  • Do use the preprocessor only to compile code conditionally.

Java is a recent language without the long-term legacy of features that C or C++ have. We don’t typically find lists of features to avoid in Java programming standards.




Debugging by Thinking. A Multidisciplinary Approach
Debugging by Thinking: A Multidisciplinary Approach (HP Technologies)
ISBN: 1555583075
EAN: 2147483647
Year: 2002
Pages: 172

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net