Improving code readability with GeneXus in Marcos Crispino's blog
The next post compares call and udp invocations in GeneXus.
Improving Code Readability
In GeneXus, when we invoke an object we have the possibility of doing it either with a call or a udp.
Let's suppose that we have a procedure that performs an operation and returns a value. For the sake of example, let's suppose that we have a Sum procedure that receives two values, &a and &b, and returns their sum.
What is the difference between invoking this procedure with a call and with a udp? The invocations would be as follows:
In GeneXus, when we invoke an object we have the possibility of doing it either with a call or a udp.
Let's suppose that we have a procedure that performs an operation and returns a value. For the sake of example, let's suppose that we have a Sum procedure that receives two values, &a and &b, and returns their sum.
What is the difference between invoking this procedure with a call and with a udp? The invocations would be as follows:
call(PSuma, &a, &b, &result) o &result = udp(PSuma, &a, &b) |
Which one is better?
From the point of view of the generated code, they are almost identical. And actually, they don't have to be different because the result must be exactly the same.
However, from the point of view of code readability, the udp is much better than the call because it tells us the intention of the invocation: to load the &result variable used to return the procedure.
Quoting from the Coding Horror blog (which in turn is quoting from a book):
"Programs must be written for people to read, and only incidentally for machines to execute".
P.S.: The word "legibilidad" (readability, in Spanish) is an actual word; I wasn't sure so I looked it up in the Real Academia dictionary...
Improving Code Readability II
Many times when we're programming, we mix the code of the function that solves a certain problem with auxiliary code, and that is an obstacle to reading the code, especially when it comes to large programs.
Below are a couple of examples...
In GeneXus, the round() function receives the number to round and the number of decimals, but the number of decimals cannot be a variable, it has to be a constant (I ask, why is that? At least with GX9u4 it works this way). So, if we want to round a value to a variable number of decimal digits, we have to do it manually.
The code that we usually use is as shown below:
El código que usamos habitualmente es como el que sigue:
The problem here is that we're writing 10 lines of code that don't solve the problem and are distracting, every time we want to assign a total sum! If this appears only once in the program, there's no problem, but when it comes up 8 times in the same procedure, we have 81 unnecessary lines of code, almost 2 pages in my monitor...
Besides, this presents another problem, which is that the code can't be reused, but this is a topic for another post.
The code above can be written much more clearly, for example:
Another example:
In some cases we have a program that does something and generates a listing or an Excel sheet which don't solve the problem but have to be generated anyway.
To be more precise, I have a program that generates an accounting entry, and also creates an Excel sheet containing the information used to generate the entry. This program has about 1,200 lines and the Excel sheet generation is mixed with the program.
Since I had to make several changes to this program, the first thing I did was move the Excel generation to subroutines. As a result, the program's code, instead of having 50 or 60 lines with &excelDoc.Cells(...), it only had one line with
do 'Excel: Graba línea'
Much clearer, isn't it?
The other three subroutines that I created were:
As a side comment, I'm liking subroutines more and more because code readability is greatly improved if we use a really descriptive name as in the cases above. It's important that we use an adequate name; for example, in the previous case, having routines called Excel1, Excel2 and Excel3 isn't a good idea.
* Posted by Marcos Crispino in his "Blog de Marcos Crispino."
From the point of view of the generated code, they are almost identical. And actually, they don't have to be different because the result must be exactly the same.
However, from the point of view of code readability, the udp is much better than the call because it tells us the intention of the invocation: to load the &result variable used to return the procedure.
Quoting from the Coding Horror blog (which in turn is quoting from a book):
"Programs must be written for people to read, and only incidentally for machines to execute".
P.S.: The word "legibilidad" (readability, in Spanish) is an actual word; I wasn't sure so I looked it up in the Real Academia dictionary...
Improving Code Readability II
Many times when we're programming, we mix the code of the function that solves a certain problem with auxiliary code, and that is an obstacle to reading the code, especially when it comes to large programs.
Below are a couple of examples...
In GeneXus, the round() function receives the number to round and the number of decimals, but the number of decimals cannot be a variable, it has to be a constant (I ask, why is that? At least with GX9u4 it works this way). So, if we want to round a value to a variable number of decimal digits, we have to do it manually.
The code that we usually use is as shown below:
El código que usamos habitualmente es como el que sigue:
Do case |
Besides, this presents another problem, which is that the code can't be reused, but this is a topic for another post.
The code above can be written much more clearly, for example:
&vLinImpTot = PedPreNet * &PedCntSdo &vLinImpTot = udp(PFRedondeaADigitosN132, &vLinImpTot, &Digitos) |
Another example:
In some cases we have a program that does something and generates a listing or an Excel sheet which don't solve the problem but have to be generated anyway.
To be more precise, I have a program that generates an accounting entry, and also creates an Excel sheet containing the information used to generate the entry. This program has about 1,200 lines and the Excel sheet generation is mixed with the program.
Since I had to make several changes to this program, the first thing I did was move the Excel generation to subroutines. As a result, the program's code, instead of having 50 or 60 lines with &excelDoc.Cells(...), it only had one line with
do 'Excel: Graba línea'
Much clearer, isn't it?
The other three subroutines that I created were:
Sub 'Excel: Crea archivo y genera cabezal' Sub 'Excel: Graba subtotales' Sub 'Excel: Finaliza' |
As a side comment, I'm liking subroutines more and more because code readability is greatly improved if we use a really descriptive name as in the cases above. It's important that we use an adequate name; for example, in the previous case, having routines called Excel1, Excel2 and Excel3 isn't a good idea.
* Posted by Marcos Crispino in his "Blog de Marcos Crispino."