Tuesday, October 17, 2006

Common sense programming

In this post I will talk a little about some common sense in organizing your code. There is nothing brilliant in this post, yet common sense seems to be the first thing we lose whenever coding something a little complicated. First, any code structure falls in one of the following:

  • sequential structures
  • logical structures (if, case)
  • repetitive structures (for, while, do)
  • others (goto, recursion, return )

I'll talk a little about each

1. Sequential structures

Most of your code is executed once in a while. Yet even this can be confusing.

First, make sure that whenever the order of the statements counts, this is obvious. Let's take an example:

calculateMarketingIncomes();
calculateSalesIncomes();
getCustomerData();

Now, suppose the sales incomes can be calculated only after the marketing ones (probably, the first procedure initializes some variables that are needed for the second). More, the customer data cannot be accessed until the sales incomes are calculated. It is obvious that the code sequence does not give a clue about this. There are a few options you can consider in cases like this:

  • give better names: you can rename calculateMarketingIncomes to InitializeDataAndCalculateMarketingIncomes. Clumsy name? Maybe you should break your code into 2 procedures, InitiailzeMarketingData and CalcultateMarketingIncomes
  • have some parameters that make the sequence logical. Have a value returned and use it in the next procedure. That should make clear that the order is important
  • if and only if you cannot change the structure of the code, have comments that explain the importance of ordering the code that way

In other cases, the order does not matter for a certain step. Yet,the statements should be grouped logically. Consider the following bad example:

object a1=new object();
object a2=new object();
object a3=new object();
a1.doStuff();
a2.doStuff();
a3.doStuff();
a1.clean();
a2.clean();
a3.clean();

The bad thing is that the code for each object is interlaced with the others. When debugging a1, you have to look at 3 times the number of lines that you normally should. A better version is:

object a1=new object();
a1.doStuff();
a1.clean(;
object a2=new object();
a2.doStuff();
a2.clean();
object a3=new object();
a3.doStuff();
a3.clean();

This way, you will have to deal with less lines when interested in one object and it will be also easy to separate the code into routines if needed.

Logical structures

Logical structures are needed whenever one needs to execute a certain piece of code depending on certain factors. First, consider using routines for checking. Instead of

if ((ch>="a" && ch<="z") ||(ch>="A" && ch<="Z"))

consider

if(isChar(ch))

where isChar is a routine defined elsewhere.

Another thing to watch for is testing for the logical condition. Instead of testing for not not false, test for true.

Whenever you have nested if conditions, make sure the normal path is easy to follow. Try to put the usual case in the if clause and the unusual in the else. This will improve both readability and performance.
Repetitive structures

First of all, make sure you use the logically right structure. It is known that in languages such as C++ you can achieve with a for everything you can achieve with a while or do...while. Yet there is no reason not to use while or do...while if that improves readability.

Try not to nest more than 3 repeatitive structures. Besides the big performance penalty, it is known that our brains have troubles in understanding them.

Beware of cross talk. It is common to use variables as i,j,k to walk through repetitive statements. Yet it is common to manipulate j instead of i. Try to give the variables more meaningful names whenever you have nested control structures.

Others

Beware of gotos. gotos are not evil, but they really decrease your code readability. Use them only if no other options are available. And think it through before deciding that you have no more options. Cases when you really need them are very, very rare.

Beware of recursion. It is an elegant solution, yet very resource intensive. First, make sure you have an exit condition. Then, make sure that there will be no stack overflow. And, most important, consider not using it. The usual examples, like computing a factorial using recursion, are plain wrong. You can do these operation with a loop and you will save lots of resources (by eliminating the overhead of a function call) in so doing.

If your language supports foreach or any other kind of looping through array structures, use it! It increases readability.

Not that everything in this post may be obvious. Yet remember these advices when you are deep into your source code.

No comments: