The issue of what constitutes good programming style is, of course, subjective, just as is the issue of what constitutes good writing style. Probably the best way to learn good programming style is to learn by example and to always keep the issue somewhere in the front of your mind while programming.
This is not the place to enter into a detailed discussion of programming style. However, in my opinion, the two most important maxims for good programming are:
When in doubt, favor readability over cleverness or elegance.
Fill your programs with lots of meaningful comments.
Let us take the second point first. It is not possible to overestimate the importance of adding meaningful comments to your programs—at least any program with more than a few lines.
The problem is this: good programs are generally used many times during a reasonably long lifetime, which may be measured in months or even years. Inevitably, a programmer will want to return to his or her code to make changes (such as adding additional features) or to fix bugs. However, despite all efforts, programming languages are not as easy to read as spoken languages. It is just inevitable that a programmer will not understand (or perhaps not even recognize!) code that was written several months or years earlier, and must rely on carefully written comments to help reacquaint himself with the code. (This has happened to me more times that I would care to recall.)
Let me emphasize that commenting code is almost as much of an art as writing the code itself. I have often seen comments similar to the following:
' Set x equal to 5 x = 5
This comment is pretty useless, since the actual code is self-explanatory. It simply wastes time and space. (In a teaching tool, such as this book, you may find some comments that would otherwise be left out of a professionally written program.)
A good test of the quality of your comments is to read just the comments (not the code) to see if you get a good sense not only of what the program is designed to do, but also of the steps that are used to accomplish the program's goal. For example, here are the comments from a short BASIC program that appears in Appendix F:
' BASIC program to compute the average ' of a set of at most 100 numbers ' Ask for the number of numbers ' If Num is between 1 and 100 then proceed ' Loop to collect the numbers to average ' Ask for next number ' Add the number to the running sum ' Compute the average ' Display the average
Readability is also a subjective matter. What is readable to one person may not be readable to another. In fact, it is probably fair to say that what is readable to the author of a program is likely to be less readable to everyone else, at least to some degree. It is wise to keep this in mind when you start programming (that is, assuming you want others to be able to read your programs).
One of the greatest offenders to code readability is the infamous
GOTO
statement, of which many languages
(including VBA) have some variety or other. It is not my intention to
dwell upon the GOTO
statement, but it will help
illustrate the issue of good programming style.
The GOTO
statement is very simple—it just
redirects program execution to another location. For instance, the
following BASIC code asks the user for a positive number. If the user
enters a nonpositive number, the GOTO
portion of
the code redirects execution to the first line of the program (the
label TryAgain
). This causes the entire program to
be executed again. In short, the program will repeat until the user
enters a positive number:
TryAgain: INPUT "Enter a positive number: ", x IF x <= 0 THEN GOTO TryAgain
While the previous example may not be good programming style, it is at least readable. However, the following code is much more difficult to read:
TryAgain: INPUT "Enter a number between 1 and 100: ", x IF x > 100 THEN GOTO TooLarge IF x <= 0 THEN GOTO TooSmall PRINT "Your number is: ", x GOTO Done TooLarge: PRINT "Your number is too large" GOTO TryAgain TooSmall: PRINT "Your number is too small" GOTO TryAgain Done: END
Because we need to jump around in the program in order to follow the possible flows of execution, this type of programming is sometimes referred to as spaghetti code. Imagine this style of programming in a program that was thousands of lines long! The following version is much more readable, although it is still not the best possible style:
TryAgain: INPUT "Enter a number between 1 and 100: ", x IF x > 100 THEN PRINT "Your number is too large" GOTO TryAgain ELSEIF x <= 0 THEN PRINT "Your number is too small" GOTO TryAgain END IF PRINT "Your number is: ", x END
The following code does the same job, but avoids the use of the
GOTO
statement altogether, and would no doubt be
considered better programming style by most programmers:
DO INPUT "Enter a number between 1 and 100: ", x IF x > 100 THEN PRINT "Your number is too large" ELSEIF x <= 0 THEN PRINT "Your number is too small" END IF LOOP UNTIL x >= 1 AND x <= 100 PRINT "Your number is: ", x END
Readability can also suffer at the hands of programmers who like to think that their code is especially clever or elegant but, in reality, just turns out to be hard to read and error-prone. This is especially easy to do when programming in the C language. For instance, as a very simple example, consider the following three lines in C:
x = x + 1; x = x + i; i = i - 1;
The first line adds 1
to
x
, the second line adds
i
to x ,
and the third
line subtracts 1
from
i
. This code is certainly readable (if not
terribly meaningful). However, it can also be written as:
x = ++x+i--;
This may be some programmer's idea of clever programming, but to me it is just obnoxious. This is why a sagacious programmer always favors readability over cleverness or elegance.
Another major issue that relates to readability is that of modular programming. In the early days of PC programming (in BASIC), most programs were written as a single code unit, sometimes with many hundreds or even thousands of lines of code. It is not easy to follow such a program, especially six months after it was written. Also, these programs tended to contain the same code segments over and over, which is a waste of time and space.
The following BASIC example will illustrate the point. Line numbers have been added for reference. (Don't worry too much about following each line of code. You can still follow the discussion in any case.)
10 ' Program to reverse the letters in your name 20 ' Do first name 30 INPUT "Enter your first name: ", name$40 reverse$ = ""
50 FOR i = LEN(name$) TO 1 STEP -1
60 reverse$ = reverse$ + MID$(name$, i, 1)
70 NEXT i
80 PRINT "First name reversed: " + reverse$ 90 ' Do middle name 100 INPUT "Enter your middle name: ", name$110 reverse$ = ""
120 FOR i = LEN(name$) TO 1 STEP -1
130 reverse$ = reverse$ + MID$(name$, i, 1)
140 NEXT i
150 PRINT "Middle name reversed: " + reverse$ 160 ' Do last name 170 INPUT "Enter your last name: ", name$180 reverse$ = ""
190 FOR i = LEN(name$) TO 1 STEP -1
200 reverse$ = reverse$ + MID$(name$, i, 1)
210 NEXT i
220 PRINT "Last name reversed: " + reverse$
Now, observe that lines 40-70, 110-140, and 180-210 (in bold) are
identical. This is a waste of space. A better approach would be to
separate the code that does the reversing of a string
name
into a separate code
module
and call upon that module thrice, as in
the following example:
' Program to reverse your name DECLARE FUNCTION Reverse$ (name$) ' Do first name INPUT "Enter your first name: ", name$ PRINT "First name reversed: " + Reverse$(name$) ' Do middle name INPUT "Enter your middle name: ", name$ PRINT "Middle name reversed: " + Reverse$(name$) ' Do last name INPUT "Enter your last name: ", name$ PRINT "Last name reversed: " + Reverse$(name$)
The separate code module to reverse a string is:
' Reverses a string FUNCTION Reverse$ (aname$) Temp$ = "" FOR i = LEN(aname$) TO 1 STEP -1 Temp$ = Temp$ + MID$(aname$, i, 1) NEXT i Reverse$ = Temp$ END FUNCTION
Of course, the saving in space is not great in this example, but you can imagine what would happen if we replace the reversing procedure by one that requires several hundred lines of code and if we want to perform this procedure a few hundred times in the main program. This modularization could save thousands of lines of code.
There is another very important advantage to modular programming. If we decide to write another program that requires reversing some strings, we can simply add our string-reversing code module to the new program, without having to write any new code. Indeed, professional programmers often compile custom code libraries containing useful code modules that can be slipped into new applications when necessary.
It is hard to overestimate the importance of modular programming. Fortunately, as we will see, VBA makes it easy to create modular programs.
Generally speaking, there are two main groups of code modules:
functions
and
subroutines
.
The difference between them is that functions return a value whereas
subroutines do not. (Of course, we may choose not to use the value
returned from a function.) For instance, the
Reverse
function described in the previous
example returns the reversed string. On the other hand, the following
code module performs a service but does not return a value—it
simply pauses a certain number of seconds (given by
sec
):
SUB delay (sec) ' Get the current time StartTime = TIMER ' Enter a do-nothing loop for sec seconds DO LOOP UNTIL TIMER - StartTime > sec END SUB
Functions and subroutines are extremely common in modern coding. Together, they are referred to as procedures .