Release 10.1A: OpenEdge Development:
Progress 4GL Handbook

> ## Dynamic programming tips

> This section provides some advice on programming with dynamic objects.

> ##### Dynamic versus static programming

> The most basic thing to consider about programming with dynamic objects is whether and when to do it at all. Since support for dynamic objects was added to the Progress 4GL in more recent releases, some developers think that dynamic constructs are inherently an improvement over their static counterparts and are tempted to program everything using dynamic objects. This is a big mistake. Dynamic objects are intended to give you more choices when you develop an application, not to replace static references to tables and fields. There are several important considerations:

> - Generally, dynamic constructs execute more slowly than their static equivalents. This is logical because, at compile time, Progress cannot anticipate what the procedure will do at run time and so cannot set up the structures to support the procedure. More of the work is done at run time, when the interpreter looks at the values of the dynamic procedure elements, just in time to prepare and execute them.

> - Dynamic programming is more difficult than static programming. Code that is full of dynamic references is usually more difficult to understand at first glance. Since important values are stored in variables rather than hard-coded into the source procedures, it is less clear what is going on when you first look at the source code. Also, the use of handles and other devices that provide you with a level of indirection makes it harder to follow what a procedure is doing without studying it carefully. It is especially important here that you provide good internal documentation in the form of comments.

> - You will usually want to write your business logic in static form, with specific references to the tables and fields (or their temp-table equivalents). Your business logic is likely to be unique to a single situation, rather than something repeated many times throughout your application. You can’t write static business logic against dynamically defined objects.

> So consider the power of dynamic programming for those situations where your application is doing the same thing in many places. Rather than writing many variations on the same source procedure or creating many compiled versions of the same procedure using include files or preprocessors, you can create a single procedure that does the same general job for every part of your application that needs it. This is why dynamic constructs exist. Consider their cost and the responsibility of having to clean up after yourself when you use them. Where they are the right thing to use, they can add tremendous flexibility to your application and dramatically reduce the number of different procedures you have to maintain.

> ##### Reusing dynamic objects

> The major section of this chapter discusses Progress memory management and emphasizes that you must delete any dynamic objects you create. It’s important to keep in mind, however, that if you are going to create another dynamic object of the same type, especially inside a loop that is executed many times, it is much more efficient to reuse the same dynamic object rather than deleting it and re-creating it. This is true even if you change all the attributes of the object. Then you must simply remember to delete it after you are completely done with it.

> For example, the following procedure needs to generate a dynamic buffer and a dynamic query for every table in the Sports2000 database. The information about tables, fields, and indexes is actually stored as schema information in the database itself, and you can access it the same way you do any other information. There are just a few bits of information you need to know to understand this example:

> - A record for each table is stored in a table called `_file`.

> - The name of the file is in a field called `_file-name`.

> - Each table has a number called the `_file-num`.

> - There are special tables in the database that have file numbers either less than zero or greater than 32K, so the procedure skips those.

>     /* h-DeleteObject.p */
>     DEFINE VARIABLE hQuery AS HANDLE      NO-UNDO.
>     DEFINE VARIABLE hBuffer AS HANDLE     NO-UNDO.
>     DEFINE VARIABLE iCount AS INTEGER     NO-UNDO.

>         ETIME(TRUE).
>         DO iCount = 1 TO 100:
>             FOR EACH _file WHERE _file-num > 0 AND _file-num  32000:
>                 CREATE BUFFER hBuffer FOR TABLE _file._file-name.
>                 CREATE QUERY hQuery.
>                 hQuery:SET-BUFFERS(hBuffer).
>                 hQuery:QUERY-PREPARE("FOR EACH " + _file._file-name).
>                 hQuery:QUERY-OPEN().
>                 hQuery:GET-FIRST().
>                 hQuery:QUERY-CLOSE().
>                 DELETE OBJECT hQuery.
>                 DELETE OBJECT hBuffer.
>             END.
>         END.
>         MESSAGE "100 iterations took " ETIME "milliseconds" VIEW-AS ALERT-BOX.

> For each database table, the code creates a dynamic query and a dynamic buffer, and sets the query’s buffer to the dynamic buffer handle. Then the code prepares a default query for the table, opens it, gets the first record, and closes it. It then deletes the dynamic query and the dynamic buffer. To show the effect of the performance more dramatically, this is all done inside a loop 100 times. Figure 21–4 shows how long it took for one sample run.

> Figure 21–4: Result of DELETE OBJECT example

> There’s a more efficient way to do this. If you move the `CREATE QUERY hQuery` statement before the `DO` block and the `FOR EACH` block, and you move the `DELETE OBJECT hQuery` statement after the end of those blocks, the procedure runs somewhat faster. Even though you are resetting the query’s buffer and re-preparing it for a completely different buffer, it is still faster to reuse the same dynamic object:

>     /* h-DeleteObject.p */
>     DEFINE VARIABLE hQuery AS HANDLE      NO-UNDO.
>     DEFINE VARIABLE hBuffer AS HANDLE     NO-UNDO.
>     DEFINE VARIABLE iCount AS INTEGER     NO-UNDO.

>         CREATE QUERY hQuery.
>         ETIME(TRUE).
>         DO iCount = 1 TO 100:
>             FOR EACH _file WHERE _file-num > 0 AND _file-num < 32000:
>                 CREATE BUFFER hBuffer FOR TABLE _file._file-name.
>                 hQuery:SET-BUFFERS(hBuffer).
>                 hQuery:QUERY-PREPARE("FOR EACH " + _file._file-name).
>                 hQuery:QUERY-OPEN().
>                 hQuery:GET-FIRST().
>                 hQuery:QUERY-CLOSE().
>                 DELETE OBJECT hBuffer.
>             END.
>         END.
>         MESSAGE "100 iterations took " ETIME "milliseconds" VIEW-AS ALERT-BOX.
>         DELETE OBJECT hQuery.

> Figure 21–5 shows the result.

> Figure 21–5: Result of more efficient DELETE OBJECT example

> Just be sure you don’t forget to delete the object when you’re done with it! Unless you’re reusing the object a large number of times, the difference in performance won’t be dramatic, so it isn’t worth the risk of forgetting to delete a dynamic object unless you will be reusing it many times in succession.

> Note that you can’t reuse the dynamic buffer in the same way. When you use the `CREATE BUFFER` statement, you must name the table the buffer will be for. This name can be an expression, as it is here, so that the buffer name can be assigned dynamically at run time, but you can’t then reuse that same dynamic buffer object for a different buffer. So you have to create it and delete it inside the loop.

Copyright © 2005 Progress Software Corporation www.progress.com Voice: (781) 280-4000 Fax: (781) 280-4095