You appreciate a well-tied knot when you untie it; you appreciate a well-structured program when you maintain it. Using naming conventions and good programming structure provide clear standards and formats that you can easily learn, follow, and understand, which makes writing and maintaining code easier. The advantages of good programming extend beyond improving the program performance. If everyone on the same project understands and uses the same names and structure, any project team member can follow and maintain the program.
Related: SQL Server Index Naming Guidelines
Setting and using standard naming conventions (e.g., format, case, special characters, etc.) play an important part in well-designed code. You also need to set and follow standards that cover code structure, such as the use of procedures, components, etc., and have methods for critical activities such as file and data access, actions in loops, etc. Using document and program storage, such as network shares or a tool such as Visual SourceSafe, is also a good idea. A well-structured program also needs to have lightly coupled and highly cohesive procedures, which I’ll explain later in this article.
Standard naming conventions are the types of names you give variables and objects that describe their function (e.g., database server, database, column, foreign key, trigger, stored procedure, etc.). It’s easier to understand the code when you can determine what a variable or object does by seeing the name. You then use the same names consistently throughout a program to organize your code, so it’s logical, and easier to read and maintain. If all developers on your team are following the same naming convention, they know what names to use and maintain consistency throughout the code. If a developer on the team gives a variable a different name from the standard name, the code will break because of the name difference.
Naming specifications don’t need to be long; they only need to be consistent. Suppose you name a variable that holds a customer name strCustomerName. The str prefix defines the variable as a string data type; thus, you’ll know you can’t give it an integer value. If you don’t know what str specifies, you can look it up in a standard naming convention list, as Table 1 shows.
The cmdSave and txtCustomerName object names in the Visual Basic (VB) code in Listing 1 illustrate standard naming conventions in the code. For example, in txtCustomerName the txt prefix specifies the type of control that a text box in a Web form or page uses. Likewise, if you add a Save button to the form or page, you can name it cmdSave. The cmd prefix specifies that this object is a command button.
The cmdSave_Click event in Listing 1 calls the SaveCustomer function, which Listing 2 shows. (I’ve omitted the actual database logic for clarity.) Naming the variable sCustomerName and the text box txtCustomerName with the same standard suffix, CustomerName, is important because it specifies that the variable and the object use the customer name. The naming convention of the function, SaveCustomer, in the function statement calls for an action verb followed by the noun, but with no prefix indicating the data type of the returned value. Second, the function returns a status value as an integer. A return value of 0 indicates success whereas any other result indicates a problem.
You also need to make global variables a part of your standards. Note how the variable sCustomerName is global in the form, but not through the rest of the code, as Listing 1 shows. Global variables can be useful, but they can also cause frustration; it’s easy to change the contents of a global variable inside of a procedure when you didn’t intend to change it for other procedures that use the variable. The effects of changing a global variable will then extend beyond the local procedure because the change will appear in all the code that uses this global variable. In some cases, it’s better to use a local variable and pass the data into and out of procedures. I’ll use the SaveCustomer function to illustrate this technique later in this article.
Another example is the error handler, SaveCustomerError, in Listing 2. The function uses an error handler to trap runtime errors. The function name, SaveCustomer, followed by the word Error clearly states the function in which the error handler belongs.
Using an Exit Function statement just before the error handler, as Listing 2 shows, is another standard you might want to implement. The Exit Function prevents the code from accidentally falling into the error handler. Also, you can use Exit Sub to exit a subroutine in procedures that don’t return a value to exit the subroutine at any time. If you are using a function, then you use Exit Function instead.
Small blocks of code are easier than large ones to create, debug, test, implement, and maintain. You’ll want to use a standard that defines how to break the code into component parts, such as subprocedures, modules, and functions. Also, breaking your code into procedural blocks makes it easier for other developers to read and understand, or you can make a block of code into a component of another program, such as SaveCustomer in Listing 2.
You can easily share a code module among different VB applications. Placing procedures in a separate code module, isolated from the rest of the application, makes them portable. Then, with slight modifications, you can use the code module as an include file for an Active Server Pages (ASP) application. You can also use a code module procedure as a class module interface.
VB’s features such as modules and classes let you easily build reusable chunks of code. For example, you can put code into a .bas file extension file to isolate the subroutines and functions. You can use Forms to isolate the user interface from the code. You can also create COM objects by placing your code into classes (.cls). These classes become COM objects at runtime.
Coupling and Cohesion
How many subroutines do you have with that one fatal design or implementation flaw that prevents you from moving them to a standard code module, much less to a method in a class? Many developers say, "I always write modular code!" This might be true, but programmers often violate the rule of coupling and cohesion in many ways, especially in languages such as VB, VBScript, C++, and Java. The coupling and cohesion rule states that procedures should be lightly coupled and highly cohesive. You can share highly cohesive, lightly coupled components more easily throughout the code. For more information about coupling and cohesion, see Steve McConnell, Code Complete (Microsoft Press). The coupling and cohesion rule provides a good guideline for writing VB applications.
Coupling. Coupling is the strength of the interconnections and interactions of program components. Coupling defines how tightly your subroutines link to other subroutines or objects. A lightly coupled subroutine receives input when it’s called and returns a value. This type of subroutine doesn’t care which routine called it. On the other hand, a tightly coupled subroutine, links directly to the routines that call it. For example, it’s normal for a tightly coupled subroutine to directly change global variables, database records, or other items throughout the program, such as control properties. A good example of a tightly coupled VB subroutine is one that directly sets a form control to a variable or database record. In VB, tight coupling typically occurs through the overuse and sharing of public symbols, such as variables, constants, properties, and routines exported by other units. Making a change to a tightly coupled method can affect other areas of your program, whereas changing a loosely coupled method usually doesn’t require code changes elsewhere in your program.
Listing 3 shows a VB application that demonstrates tight coupling. When you click the GetTitle1 button, as Screen 1 shows, the cmdGetTitle1_Click event in Listing 3 executes. This event uses ADO to extract a list of titles from the Pubs database. Then the event loads the titles in the lstTitles control. Note that the cmdGetTitle1_Click event contains all the code. This code is tightly coupled because it links the lstTitles control to the subroutines (event) code.
On the other hand, when you click the GetTitle2 button, the cmdGetTitle2_Click event in Listing 4 executes. This event uses almost the same code as cmdGetTitle1_Click to extract the data and load the list. The difference between this listing and Listing 3 is in the function that extracts the data. The GetTitle function extracts the data from the database and returns it in an ADO recordset as the function’s return value. The GetTitle function is loosely coupled because it isn’t tied to any other code. The cmdGetTitle2_Click has a little extra code to handle the return value from GetTitle, but other than that, the code loads the list box in the same way as the code in cmdGetTitle1_Click.
The big difference between these two click events is how the code in one instance is reusable and in the other, it’s not. You could move the GetTitle function to a code module and it would work the same way it does in Listing 4. You could also move GetTitle directly into a class module. You can’t easily reuse the code in cmdGetTitle1_Click, in Listing 3, because it’s directly linked to lstTitles, and VB can’t call it from other types of code that don’t need to load lstTitles.
Cohesion. Cohesion is the degree of functional relatedness with other components. A highly cohesive subroutine, function, or method is designed to perform only one task (occasionally two or three tasks) extremely well. The cohesive subroutine doesn’t perform a myriad of different and unrelated functions. A highly cohesive method might have a name that represents its sole function, such as CalculateInterest; whereas, you might call a non-cohesive method CalculateInterestOrTaxOrHeatIndex. Note that the latter method performs three unrelated tasks, whereas the first method performs only one.
Listing 3 and Listing 4 also include a simple example of cohesion. The GetTitle function is highly cohesive because it handles only one function; it retrieves titles from the Pubs database and returns the titles in a recordset. This is an example of effective cohesion because of the GetTitle function’s single-task and lightly coupled nature. The cmdGetTitle1_Click event isn’t highly cohesive because it does two things; it retrieves the titles from the Pubs database, and it loads lstTitles directly. GetTitle’s high cohesion makes it more usable because you can call it from anywhere. Coupling and cohesion go hand in hand to create well-built subroutines and functions. The coupling and cohesion rule is also important when you convert applications into components. This rule relates to COM methods and converting applications to objects because it’s easier to convert the object with less coupling between your subroutines and functions. More cohesive existing subroutines and functions are easier to convert because they are not riddled with code references to other places and they perform granular tasks. The time you invest in the design and implementation of your application pay off—reusable code lets you spend less time designing code.
The larger number of subroutines and functions that typically result from implementing the coupling and cohesion rule can affect the speed of your code. However, the impact is negligible, and other elements in the code affect the application’s performance more than whether you implement a piece of code as a subroutine rather than inline in the code. Also, Microsoft and other software applications contain subroutines and functions that do most of the application’s work. In fact, most of the feature-rich applications use functions and COM to do the work. However, you’ll pay a performance penalty for using COM when you make out-of-process calls to the interface of an object. Also, today’s processors and other system hardware perform so fast that it makes up for any small performance loss you incur from using functions.
Structuring for Maintenance
The real cost of your application is its maintenance. Maintenance has traditionally been 50 percent to 80 percent or more of an application’s cost, whereas construction is only about 20 percent. Functions and well-structured code can drastically reduce the maintenance cost and make you more productive.
COM in Structured Code
Performance with COM relates directly to the use of structured code. For example, you put subroutines and functions in a class module and access them using method calls to the class that contains them. Performance-wise, this technique compares favorably to using subroutines and functions in a .bas module. For in-process components, calling a method has roughly the same performance cost as calling the same function in a .bas module. In other words, the tradeoff is so small you don’t notice it.
Structured programming with good naming conventions is a practice that’s simple to master after you learn the concepts and apply them when you write code. If you want a good reason to learn structured programming, try to enhance or maintain a program that someone else wrote. You’ll discover how well-structured programming works and why you should use it.