Creating software can be considered an art form, requiring all of the characteristics associated with an artist, such as creative style. Most artists, however, aren't required to modify their creations after the work has been purchased. Software, on the other hand, needs to be maintained either by its creator or by others if the creator has moved to other responsibilities.
The maintainability of software is an increasingly important issue. As industry demand for programmers' skills increases, so does their turnover rate within a company, thus diminishing the likelihood of the original programmer's being available when a problem arises or when the product needs to be enhanced. Also, a programmer who produces volumes of code may need some time to refamiliarize him- or herself with code that was written only a month earlier, perhaps, but hundreds of lines ago.
Programmers exposed to code that has a style different from their own may institute parts of the new style into their own code if there's an obvious advantage. Most programmers restyle inherited code to their own conventions. New programmers should thus be exposed to good coding style and its advantages before any code is written.
One useful coding technique benefiting both code readability and writability and hence maintainability is Hungarian Notation. This technique is suitable for C/C++ programs that make use of pointers. It's also applicable to Java programs. Essentially, Hungarian Notation is a naming convention that allows a programmer to determine the type of variable or constant just by looking at its name. The letter "i", for example, can be used to denote an "int". By prefixing an "int" variable named "count" with an "i" as in "iCount", a programmer can tell by looking at the variable name that the variable is of type "int".
As an example, look at the following code segment:
float x = 0;
int y = 0;
x = y;
Looking only at the statement "x = y", someone attempting to understand this code might assume that both variables are of the same type. A good guess would be that x and y are coordinate variables of type "int". However, we don't know if x and y are objects, numbers or characters. This is especially true if the declarations for x and y are not physically close to the assignment statement. You'd need to search through the code looking for the declarations. If the original programmer had used Hungarian Notation, the statements would look like this:
float fX = 0;
int iY = 0;
fX = iY;
A person reading just the statement "fX = iY" can deduce that an "int" is being assigned to a "float" and may suspect that the code is a little out of the ordinary. Now look at these statements:
int iX = 0;
float fY = 0;
iX = fY;
From reading just the statement "iX = fY", a person can now deduce that a float is being assigned to an integer and be very concerned. Because of a type mismatch this program won't compile successfully.
As a side benefit, Hungarian Notation allows the original programmer to recognize that an explicit cast is required in an assignment statement while the code is being written. A programmer writing the above statement would notice that "iX" and "fY" were of different types and would insert a cast "(int)" before entering the right side of the assignment, as in "iX = (int)fY", or might change "iX" to a float variable, "fX".
This is the writability aspect of using Hungarian Notation, in which errors are detected and removed as the code is written. Discovering errors earlier in the development cycle is a natural benefit.
Reuse of Variable Names
Hungarian Notation also allows variable names to be reused when only the notation part of the name is different. Let's take a look at a slightly more complex example, shown in Listing 1. In this class the constructor is given a file name and a "boolean." The file name is the name of a file and the boolean is used to tell the constructor whether the file name should be used.
In this example an underscore character prefix distinguishes the instance variables
from the local variables and function arguments. Also the letter "b" is used as the
notation prefix for boolean type variables, as in "_bNameIsValid". The variable name
"strFilename" is used for both the input argument and the instance variable with the
exception of the underscore prefix in the instance variable. If you just see the
_strFilename = strFilename;
_bNameIsValid = true;
you can tell immediately which variables are instance variables, booleans or strings,
and - if the original programmer used sensible variable names - the purpose of each
variable. Most important, the assignment statements look correct from a "type" point
of view: a "String," is assigned a "String" and a "boolean" is assigned a "boolean"
The same variable name can also be used for related variables. The Hungarian Notation
prefix provides a distinction between the data type of the variables. For instance,
the following two state variables represent the string for a "Cancel" button and the
Button object itself.
String strCancel = new String(); // The 'Cancel' string
Button btnCancel = new Button( strCancel ); // The cancel button
The following statements ask a user to enter a user ID. A JLabel is used to identify a JTextField as the place to enter the user ID. Both variables are named "Userid" but are distinguished by their particular data type notation.
JLabel labelUserid = new JLabel
( "Enter your user id here:" );
JTextField fieldUserid = new JTextField();
Search and Replace
When writing new code or upgrading existing code, notated variables are readily modifiable. For instance, a text editor's replace utility can rename all instances of a variable named "iW" to "iX" in one operation. However, the same operation can't be used to rename variable "w" to "x" without errors as all instances of the letter "w" would be replaced whether or not it represents a variable.
Variable Naming Conventions
The following is a suggested set of rules for generating variable names and is not meant to be a rigid set.
- The prefix should be determined from the datatype definition.
- The prefix should be in lowercase only.
- If the variable is declared inside a class as instance data, the prefix should be prefixed by an underscore as in "_fVarName".
- All words in the variable name should be capitalized to increase readability, starting with the first letter after the notation prefix.
- Don't overdo it. If you have a single JPanel, name it "panel."
- For longer type names such as FontMetrics, use either an acronym such as "fm" or a portion of the name such as "metric."
Here are some name prefix suggestions:
Append the letter "a" to the type prefix:
byte - yaNumbers;
int - iaNumbers;
String - straNames;
Integer - intIndex;
String - strIndex;
StringBuffer - strbIndex;
Point - ptIndex;
Boolean - boolFound;
Label - labelTextString;
TextField - fieldName;
Panel - panelTextArea;
Frame - frameWindow;
Window - wndPrompt;
Popup - popSelection;
MyOwnObjectType - mootPoint;
Prefix a notation for the object type to the notation for the variable type.
// Vector of Integer objects
// Hashtable of Serializable address
Add an underscore before the type prefix and the variable name.
byte - _yaNumbers;
int - _iIndex;
boolean - _bFound;
float - _fInches;
String - _strName;
As an alternative, another designation such as "m_" can be added to the beginning of a variable name to indicate an instance (member) variable.
byte - m_yaNumbers;
int - m_iIndex;
boolean - m_bFound;
float - m_fInches;
String - m_strName;
Prefix a "c_" to the name.
int - c_iIndex;
double - c_dValue;
A Life Example
Say tomorrow someone came to you and said a fellow developer is unable to complete a critical piece of code and you're the only person who has the skills and knowledge to finish the job and by the way, you only have until the end of the week to complete the task. Otherwise the customer will cancel the contract and take that six-figure sum elsewhere.
Before you panic, you decide to evaluate the situation by reading a sample of the code. You need to find out whether it looks reliable and whether you can understand it well enough to modify it. The answers to these questions, multiplied by a hundred, will give you a good idea of how you'll react when asked to inherit the entire class library. You arbitrarily turn to the function shown in Listing 2. What does it do?
This code is actually part of a text editor class that searches for a given string with optional case sensitivity within a vector of string buffers beginning at an x and y coordinate.
The "result" returned is the point coordinate of the found text, y being the row and x being the column, or (-1, -1) if the text string isn't found. From this code we can deduce several things:
- The original programmer was kind enough to use understandable variables, not just "s"or "zx".
- The variables "numberOfLines," "lines" and "cursor" aren't declared in the function so they're probably instance or class variables, but we're not really sure and we'll have to hunt for them.
- The author may have had difficulty with variable naming since there are similarly named variables "line," "lines" and "line1".
- The variable "lines" is probably a "Vector" since on line 25 we see a "lines.elementAt()" function call. Then again, it could be a "DefaultListModel".
- The variable "cursor" is probably a "Point" since on lines 8 and 9 we see "cursor.x" and "cursor.y". However, it could also be an "Event" object or, less likely, a "Rectangle" object.
- The variable "numberOfLines" is probably an "int" since it's being assigned to another "int" on line 23.
Looking at the individual statements from line 25 to line 29, it's difficult to determine whether the type assignments are correct and the method calls against the variables are appropriate. Could this be the real source of the term fuzzy logic?
Now compare the code in Listing 2 to the same code in Listing 3, which has been written using Hungarian Notation.
Several things are worth noticing here:
- The "cases" variable has been replaced with "bCase", which clearly defines the variable as a "boolean" intended to mean case sensitivity. Since "case" is a keyword, the original author couldn't use it as a variable name, which is probably why the variable was originally named "cases".
- The variables "_iNumberOfLines", "_vstrbLines" and "_ptCursor" are clearly defined as instance variables of type "int", "Vector" and "Point", respectively.
Each statement from line 25 to line 29 can now be examined individually, out of context, to determine whether each line's syntax is correct. For example, let's visit line 25.
- The variables named "line", "lines" and "line1" have been replaced by "strbLine", "vstrbLines" and "strLine", signifying a "StringBuffer", a "Vector" of StringBuffers and a "String". The same root name "Lines" has been reused for all three variables, which eliminates the extra step of having to think up different but similar variable names that represent the storage of a text string.
25 strbLine = (StringBuffer)_vstrbLines.elementAt( i );
Here's what we can deduce:
- The function "elementAt()" is correctly called against the object "_vstrbLines" since the "v" indicates a "Vector".
- The argument type to the function "elementAt()" is correct since the variable "i" is an "int".
- The "(StringBuffer)" cast against "_vstrbLines.elementAt(i)" is correct since the "Vector" holds objects of type "StringBuffer" as indicated by the "strb" in "_vstrbLines".
- The left side of the assignment statement is also correct since the result of the cast is being assigned to a "StringBuffer" as indicated by the "strb" in "strbLine".
The statement is clear and easier to read because it contains more information than the original. More important, it's easier to write because as a developer who uses Hungarian Notation consistently, you can tell the statement is correct as you write it.
Differences in artistic coding style can vary greatly among programmers. The personal aspects of coding style are associated with pride of ownership. However, a lack of standards can indirectly produce code that has unknown reliability characteristics and is hard to maintain. The result can be unplanned work of unknown duration, which includes tasks ranging from documenting to rewriting. It's financially beneficial to use accepted coding techniques, such as Hungarian Notation, consistently. This will provide long-term time savings. Its use can result in code that is more readable, writable, understandable and thus more maintainable, transferable and reliable. Think about this the next time you inherit someone else's code or would like to pass on some of your own code to that person who's looking over your shoulder.
Brian Farn researches and develops Java software at IBM and is currently associated with the IBM VisualAge for Java team at the IBM Software Solutions Toronto Laboratory. He is a
graduate of the University of Western Ontario with degrees in physics and electrical engineering. He can be reached at [email protected]
public class Sample
String _strFilename = null;
boolean _bNameIsValid = false;
public Sample( String strFilename, boolean bUseFilename )
// Store the filename if it is valid, and if told to
if( bUseFilename == true && strFilename != null )
if( strFilename.length() > 0 )
_strFilename = strFilename;
_bNameIsValid = true;
1 public Point find( String find,
boolean cases )
3 Point result = new Point( -1,-1 );
4 StringBuffer line = null;
5 String needle = null;
6 String hay = null;
7 int index = 0;
8 int x = cursor.x;
9 int y = cursor.y;
11 needle = cases == false ?
12 find.toUpperCase() : find;
14 line = (StringBuffer)lines.ele-
mentAt( y );
16 if( x >= line.length() - 1 )
18 x = 0;
20 if( y >= numberOfLines ) return
23 for( int i=y; i<numberOfLines;
25 line = (StringBuffer)lines.ele
mentAt( i );
26 String line1 = line.toString();
27 hay = cases == false ?
28 line1.toUpperCase() : line1;
29 index = hay.indexOf( needle, x );
31 if( index != -1 )
33 result.x = index;
34 result.y = i;
38 else x = 0;
41 return result;
1 public Point find( String strFind,
boolean bCase )
3 Point ptResult = new Point( -1,-1 );
4 StringBuffer strbLine = null;
5 String strNeedle = null;
6 String strHay = null;
7 int iIndex = 0;
8 int iX = _ptCursor.x;
9 int iY = _ptCursor.y;
11 strNeedle = bCase == false ?
12 strFind.toUpperCase() : strFind;
14 strbLine = (StringBuffer)_vstr
bLines.elementAt( iY );
16 if( iX >= strbLine.length() - 1 )
18 iX = 0;
20 if( iY >= _iNumberOfLines ) return
23 for( int i=iY; i<_iNumberOfLines; ++i )
25 strbLine = (StringBuffer)_vstr
bLines.elementAt( i );
26 String strLine = strbLine.toString();
27 strHay = bCase == false ?
28 strLine.toUpperCase() : strLine;
29 iIndex = strHay.indexOf( strNeedle, iX );
31 if( iIndex != -1 )
33 ptResult.x = iIndex;
34 ptResult.y = i;
38 else iX = 0;
41 return ptResult;