Chapter 14. Organizing Straight-Line Code

cc2e.com/1465

Contents

Related Topics

This chapter turns from a data-centered view of programming to a statement-centered view. It introduces the simplest kind of control flow: putting statements and blocks of statements in sequential order.

Although organizing straight-line code is a relatively simple task, some organizational subtleties influence code quality, correctness, readability, and maintainability.

Statements That Must Be in a Specific Order

The easiest sequential statements to order are those in which the order counts. Here's an example:

Example 14-1. Java Example of Statements in Which Order Counts

data = ReadData();
results = CalculateResultsFromData( data );
PrintResults( results );

Unless something mysterious is happening with this code fragment, the statement must be executed in the order shown. The data must be read before the results can be calculated, and the results must be calculated before they can be printed.

The underlying concept in this example is that of dependencies. The third statement depends on the second, the second on the first. In this example, the fact that one statement depends on another is obvious from the routine names. In the following code fragment, the dependencies are less obvious:

Example 14-2. Java Example of Statements in Which Order Counts, but Not Obviously

revenue.ComputeMonthly();
revenue.ComputeQuarterly();
revenue.ComputeAnnual();

In this case, the quarterly revenue calculation assumes that the monthly revenues have already been calculated. A familiarity with accounting—or even common sense— might tell you that quarterly revenues have to be calculated before annual revenues. There is a dependency, but it's not obvious merely from reading the code. And here, the dependencies aren't obvious—they're literally hidden:

Example 14-3. Visual Basic Example of Statements in Which Order Dependencies Are Hidden

ComputeMarketingExpense
ComputeSalesExpense
ComputeTravelExpense
ComputePersonnelExpense
DisplayExpenseSummary

Suppose that ComputeMarketingExpense() initializes the class member variables that all the other routines put their data into. In such a case, it needs to be called before the other routines. How could you know that from reading this code? Because the routine calls don't have any parameters, you might be able to guess that each of these routines accesses class data. But you can't know for sure from reading this code.

Visual Basic Example of Statements in Which Order Dependencies Are Hidden

When statements have dependencies that require you to put them in a certain order, take steps to make the dependencies clear. Here are some simple guidelines for ordering statements:

Organize code so that dependencies are obvious. In the Microsoft Visual Basic example just presented, ComputeMarketingExpense() shouldn't initialize the class member variables. The routine names suggest that ComputeMarketingExpense() is similar to ComputeSalesExpense(), ComputeTravelExpense(), and the other routines except that it works with marketing data rather than with sales data or other data. Having ComputeMarketingExpense() initialize the member variable is an arbitrary practice you should avoid. Why should initialization be done in that routine instead of one of the other two? Unless you can think of a good reason, you should write another routine, InitializeExpenseData(), to initialize the member variable. The routine's name is a clear indication that it should be called before the other expense routines.

Name routines so that dependencies are obvious. In the Visual Basic example, ComputeMarketingExpense() is misnamed because it does more than compute marketing expenses; it also initializes member data. If you're opposed to creating an additional routine to initialize the data, at least give ComputeMarketingExpense() a name that describes all the functions it performs. In this case, ComputeMarketingExpenseAndInitializeMemberData() would be an adequate name. You might say it's a terrible name because it's so long, but the name describes what the routine does and is not terrible. The routine itself is terrible!

Cross-Reference

For details on using routines and their parameters, see Chapter 5.

Use routine parameters to make dependencies obvious. Again in the Visual Basic example, since no data is passed between routines, you don't know whether any of the routines use the same data. By rewriting the code so that data is passed between the routines, you set up a clue that the execution order is important. The new code would look like this:

Example 14-4. Visual Basic Example of Data That Suggests an Order Dependency

InitializeExpenseData( expenseData )
ComputeMarketingExpense( expenseData )
ComputeSalesExpense( expenseData )
ComputeTravelExpense( expenseData )
ComputePersonnelExpense( expenseData )
DisplayExpenseSummary( expenseData )

Because all the routines use expenseData, you have a hint that they might be working on the same data and that the order of the statements might be important.

In this particular example, a better approach might be to convert the routines to functions that take expenseData as inputs and return updated expenseData as outputs, which makes it even clearer that the code includes order dependencies.

Example 14-5. Visual Basic Example of Data and Routine Calls That Suggest an Order Dependency

expenseData = InitializeExpenseData( expenseData )
expenseData = ComputeMarketingExpense( expenseData )
expenseData = ComputeSalesExpense( expenseData )
expenseData = ComputeTravelExpense( expenseData )
expenseData = ComputePersonnelExpense( expenseData )
DisplayExpenseSummary( expenseData )

Data can also indicate that execution order isn't important, as in this case:

Example 14-6. Visual Basic Example of Data That Doesn't Indicate an Order Dependency

ComputeMarketingExpense( marketingData )
ComputeSalesExpense( salesData )
ComputeTravelExpense( travelData )
ComputePersonnelExpense( personnelData )
DisplayExpenseSummary( marketingData, salesData, travelData, personnelData )

Since the routines in the first four lines don't have any data in common, the code implies that the order in which they're called doesn't matter. Because the routine in the fifth line uses data from each of the first four routines, you can assume that it needs to be executed after the first four routines.

Visual Basic Example of Data That Doesn't Indicate an Order Dependency

Document unclear dependencies with comments. Try first to write code without order dependencies.Try second to write code that makes dependencies obvious. If you're still concerned that an order dependency isn't explicit enough, document it. Documenting unclear dependencies is one aspect of documenting coding assumptions, which is critical to writing maintainable, modifiable code. In the Visual Basic example, comments along these lines would be helpful:

Example 14-7. Visual Basic Example of Statements in Which Order Dependencies Are Hidden but Clarified with Comments

' Compute expense data. Each of the routines accesses the
' member data expenseData. DisplayExpenseSummary
' should be called last because it depends on data calculated
' by the other routines.
InitializeExpenseData
ComputeMarketingExpense
ComputeSalesExpense
ComputeTravelExpense
ComputePersonnelExpense
DisplayExpenseSummary

This code doesn't use the techniques for making order dependencies obvious. It's better to rely on such techniques rather than on comments, but if you're maintaining tightly controlled code or you can't improve the code itself for some other reason, use documentation to compensate for code weaknesses.

Check for dependencies with assertions or error-handling code. If the code is critical enough, you might use status variables and error-handling code or assertions to document critical sequential dependencies. For example, in the class's constructor, you might initialize a class member variable isExpenseDataInitialized to false. Then in InitializeExpenseData(), you can set isExpenseDataInitialized to true. Each function that depends on expenseData being initialized can then check whether isExpenseDataInitialized has been set to true before performing additional operations on expenseData. Depending on how extensive the dependencies are, you might also need variables like isMarketingExpenseComputed, isSalesExpenseComputed, and so on.

This technique creates new variables, new initialization code, and new error-checking code, all of which create additional possibilities for error. The benefits of this technique should be weighed against the additional complexity and increased chance of secondary errors that this technique creates.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.135.213.212