Prevention

 < Day Day Up > 



Discipline is the main virtue required to prevent this illness. Learn to always consider the name you give every variable, method, class, and so on. If you do this consistently, you will be well on your way to writing more understandable code. There are, however, some concerns that must be answered and some tips to help you maintain this discipline.

Editors: Tools of the Trade

Clear names need to be of a reasonable length, and this is where the most resistance is encountered to adopting good naming schemes. Except for rare special circumstances, memory usage is no longer a concern when it comes to name length. The primary argument that remains, therefore, is that typing longer names is time consuming. This argument fails in the face of ever-advancing editor technology. When we talked about Premature Optimization of the source code, we introduced many of the technologies that remove much of the typing from long variable names. Auto-completion stands out as the primary improvement that eliminates much of this typing. As discussed, this context-sensitive technology enables the programmer to type only a few characters before the rest of the name can be automatically completed by the editor (Figure 7.1). Learning to take advantage of this technology is important to improving code readability while maintaining efficiency.

click to expand
Figure 7.1: Improved auto-completion with Visual Assist makes it easier to enter long names in Microsoft Visual Studio.

Some claim that these longer names make the code less readable rather than more readable, but this is rarely the case in practice. It is true that the names can become too verbose, which is a danger that must be avoided as well. However, this is rarely the main problem, as most programmers err on the side of names that are too short and unclear. While we should strive to achieve balance between length and understandability, it is better to err on the side of understandability. A name that is too long can be refactored by anyone to be a better length, but a name that is too short is much more difficult to lengthen because the meaning might not be obvious.

Naming Conventions

Establishing naming conventions is important to providing consistent names across a project. These conventions also assist in maintaining discipline because there is a permanent reference upon which to base name choices. In some cases, such as Java, the language community will have adopted certain standard naming conventions. This is even more beneficial as it allows code from different developers to share some common features that make reading easier.

  • Class names should always start with a capital letter, and each subsequent new word should be capitalized as well.

  • Constants should be all uppercase letters, separating words with underscores.

  • All other methods, variables, and other named constructs should begin with a lowercase word with all subsequent new words capitalized.

  • Method names should follow the general form of a verb followed by a noun, with other possible modifiers added in.

  • Boolean tests should generally begin with is or has.

  • Methods that return the internal state of an object should start with get.

  • Methods that set the internal state of an object should start with set or change and end with To.

  • Methods that perform processor-intensive calculation should begin with compute or calculate.

  • Fields should generally be nouns, except in the case of boolean flags, which follow rules similar to the functions that retrieve these values.

  • Arguments should be prefixed with io_ if they are input and output values, o_ if they are output values, or no prefix if they are input only values.

  • Local variables should be prefixed with l_ if they have the same name as an argument.

  • Template arguments should begin with t_.

Many of the conventions in this example are specific to the C++ language, but all languages can benefit from good naming conventions. Always check to see if naming conventions exist, and follow them. This will benefit those who read your code in the future, and assist you in the creation of names, which can always be a difficult process.

There is one common rule that should be followed across all languages. Just as you would do when designing interfaces, strive to make your names minimal and complete. This rule follows directly from the fact that the names are part of the interface to a class and are therefore automatically part of the minimal and complete goal. A complete name is one that requires little or no explanation to determine what action it will perform or what it represents. A minimal name is one that requires minimal space, allowing for easier reading by eliminating the need to parse an entire diatribe for just one name.

Everyday Language

We have offered some suggestions on why the variables should be of sufficient length and how to maintain the discipline needed to write good names, but what do we do to create better names? A good name should make clear what the code element represents in as succinct a form as possible. Naming conventions might also provide some guidance on what name to choose.

Perhaps the best method for choosing a good name, however, is to base the choice on how the resulting name will read in the code. This means that any programmer who is reading the code should be able to parse it as easily as he would normal everyday text such as you would find in a book or newspaper. While it is not possible to write text exactly as you would in your normal language, the choice of good names can make the code easily translatable into that normal language as the reader parses it.

 CD-ROM  The following Java snippet, also available on the companion CD-ROM in Source/JavaExamples/com/crm/ppt/examples/chapter7/ReadableCode.java, illustrates this technique:

   //... /** Interpolation key string. */    private static final String INTERPOLATION = "INTERPOLATION";    /** Linear interpolation string value. */    private static final String LINEAR = "LINEAR";    /** Spline interpolation string value. */    private static final String SPLINE = "SPLINE";    /** Current configuration. */    private final Configuration configuration = new Configuration();    /** Configuration stored as key/value string pairs. */    private class Configuration {       /** Has the configuration been fully filled in. */       private boolean isInitialized;       /** Store key/value pairs in a hash map for easy lookup. */       private final HashMap map = new HashMap();       /** Proxy class to make value access more readable. */       public class Value {          /** Internally stored string value. */          private String value;          /**           * Check if value matches string.           *           * @param value string to compare value to           * @return true if string match  *      according to equals, false if           *       null or comparison is false           */          public boolean is(String value) {             if(value == null) {                return (false);             }             return (value.equals(this.value));          }          /**           * Set the string of this value.           *           * @param value new string           */          public void to(String value) {             this.value = value;          }       }       /**        * Change the initialization state of  * this configuration.        *        * @param isInitialized true means the  *      configuration is initialized,        *       false means it is not initialized        */       public void changeInitializationFlagTo( boolean isInitialized) {          this.isInitialized = isInitialized;       }       /**        * Check to see if configuration is initialized.        *        * @return true if configuration is  *      ready, false otherwise        */       public boolean isInitialized() {          return (isInitialized);       }       /**        * Check to see if a key exists in the  * configuration.        *        * @param key string value of key to lookup        * @return true if key exists, false if  *      key does not        */       public boolean has(String key) {          return (map.containsKey(key));       }       /**        * Get the value of a key in order to  * change its value. If the key/value pair  * does not exist it is created.        *        * @param key string value of key to set        * @return value to set or change string of        */       public Value setValueOf(String key) {          if(!map.containsKey(key)) {             map.put(key, new Value());          }          return (Value)(map.get(key));       }       /**        * Get the value of a key, if the  * key/value pair does not exist        * the value represents a null String.        *        * @param key string value of key to get        * @return value representing string        */       public Value getValueOf(String key) {          if(!map.containsKey(key)) {             return (new Value());          }          return (Value)(map.get(key));       }    }    /**     * Initialize the configuration for this example.     */    public void initialize() {       configuration.setValueOf( INTERPOLATION).to(LINEAR);       configuration.changeInitializationFlagTo(true);    }    /**     * Draw key frame function designed to  * illustrate readable code naming.     */    public void drawKeyFrame() {       if(configuration.isInitialized() && configuration.has(INTERPOLATION)) {          if(configuration.getValueOf( INTERPOLATION).is(LINEAR)) {             drawThisAt( resultOfLinearCalculation());          } else if(configuration.getValueOf( INTERPOLATION).is(SPLINE)) {             drawThisAt( resultOfSplineCalculation());          }       }    }

First, look at the initialization function, which can be read as:

For the configuration, set the value of interpolation to linear. Also, for the configuration, change the initialization flag to true.

Notice that we only had to fill in a few words to get this to read as ordinary language. This was accomplished mostly through appropriate naming, with a little help from a proxy class to allow the word to to be placed in the correct location. This proxy class allows us to provide much more readable code with minimal overhead. Notice also that it can provide extra error checking and debugging possibilities if necessary.

Now look at the drawing function, which can be read as:

If the configuration is initialized and has an interpolation value, then, if the configuration interpolation is linear, draw this object at the result of a linear calculation; otherwise, if the configuration interpolation is spline, draw this object at the result of a spline calculation.

Notice that this also only requires the addition of a few added words to be read correctly and easily. Because the words and semantic structure are similar to everyday language, code reading can be done much faster. Even more important, the code can also be understood more quickly because of the natural language processing capabilities of our brain that we have been developing for much longer than our knowledge of any programming language.



 < Day Day Up > 



Preventative Programming Techniques. Avoid and Correct Common Mistakes
Preventative Programming Techniques: Avoid and Correct Common Mistakes (Charles River Media Programming)
ISBN: 1584502576
EAN: 2147483647
Year: 2002
Pages: 121
Authors: Brian Hawkins

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