Progress OO "Reflection"

enoon

Member
Is there a way to call a method of an object, if we know the method name at runtime? Some kind of "reflection" in Progress, like in procedural: RUN VALUE() or DYNAMIC-FUNCTION but in a class?
 
There is a DYNAMIC-NEW, but I don't recall any dynamic method calls. Personally, I would regard such a thing as anathema since you are throwing away the advantage of compile-time checking as well as destroying your ability to analyze the call graph.

The right way to do things like this sort of thing is to define an interface for the common functions which you would like a group of classes to exhibit and to have the classes implement that interface.

ABL reflection is pretty primitive at this point, although it is forecast to get better, but this sort of question always has me imagining code which looks through an object's method and says, "oh, this looks interesting; let me try running that". I don't see what such behavior has to do with good OOAD.
 
Unfortunately the design pattern has already been defined, since we have an old procedural-with-shared-variables-and-includes framework, and want to add some new functionality: make use of OO inheritance (to reduce the codebase) but we have such parts of the code that requires this behavior:

Code:
RUN VALUE("{&event}'" + szField + "'") (szField, iIndex, iSubIndex, szContents) NO-ERROR.

Where szField is variable known only at runtime.

Any suggestion on how to rewrite this in OO?
 
In my opinion you must not want to change procedural 4GL code to OO. Since the nature of OO is very much different then the nature of a procedural approach.
If you really want to start OO then the best starting point is to start thinking what the (part of) application does. And use that to redesign the (specific part) of your application. UML is a good way of visualizing the proces.
First start with the use case, and then extent that to the technical implementation in OO. The rules you have to follow for making an OO application are (most probably) different then you are used to with the procedural approach.

You will find out that the specific code part you are referring to is merely there to shorten things and there is no functional reason to solve the business problem that way. Seeing that will give you the oportunity to rethink the technical implementation of a specific functional requirement.
This way you give more focus to the functional requirement instead of trying to find ways of translating one techncal implementation to antoher.

Regards,

Casper.

BTW Nice town: Cluj :-)
 
Thanks for the answers. But "there is no other option" beside OO as the client requested, so we found a workaround with hardcoded procedure names and if conditions. We thought that there is some other way we are not aware of, a more elegant one.

Casper: Indeed nice town, from ES?
 
Ok, I know how custmers can be ;-)

Maybe you could try to make a method in a class which excepts the value for the run as a parameter and then you just put the code run value(blabla) in the method?
I just wanted to point out that there is no such thing as translating 4GL to 00 because the design aproach tends to be completely different and IMO the starting point of a (new) application should be decided carefully in order to not run into problems later on. One of the benefits of OO is reusability and if your design is not towards this then the benefit of OO will be lost, because in a later stage one will find out that the code you wrote in 'OO' will have to be rewritten (again).

Casper.

(Yes ES :-))
 
Someone needs to step back and have a think here. RUN VALUE is an unfortunate construct to start with, although it seemed like it was justified in some cases in 1985. This is no longer 1985 and one should not be creating new OOABL code which is a slavish imitation of V3 coding styles. The customer may want OO ... good for them ... but do they want bastard, cobbled, hamstrung OO that will be just as hard to maintain as what they already have or do they want to actually clean something up in the process of moving to OO so that they have something better when you get done than they have now?

You might want to check out http://www.cintegrity.com/content/Request-Expression-Interest to see if there is something there which could help you to provide better service for your customer.
 
Yet, run value is not always a sign of bastard, cobbled, hamstrung OO ;).
You will always have exceptions.

Take for example a report daemon where one of the parameters of incoming requests is the report name. What else can this daemon do but use run value to run the report ?
 
Of course, if it is OO, then it isn't going to RUN anything, it is going to NEW something.

There is a big difference between establishing a principle, trying to adhere to it, and then once in a long while violating it for a compelling reason versus saying "oh, there are exceptions" and not bothering to follow the principle at all.

Any kind of dynamic code presents a major obstacle to analysis. It might seem like a clever, easy way to get things done in the short run, but it can be an expensive shortcut in the long run because you have no automated way, short of a product like Joanju Analyst, for following the call graph.
 
Hi,

Finally we found a solution to our problem.

We had a dialog.cls (a generic class) and let's say dialog1.cls a particular dialog. The workaround we found includes a dataset (using it's triggers), a dispatcher and it looks like this:

dialog.cls

Code:
METHOD PUBLIC VOID invokeMethod (INPUT methodName AS CHAR, 
                                 INPUT szField    AS CHAR,
                                 INPUT iIndex     AS INT,
                                 INPUT iSubIndex  AS INT,
                                 INPUT szContents AS CHAR):
                                    
   EMPTY TEMP-TABLE TmpPar.
   /* pass parameters to the method through this temp-table which is encapsulated in a dataset */
   CREATE TmpPar.
   ASSIGN TmpPar.szField = szField
              TmpPar.iIndex     = iIndex
              TmpPar.iSubIndex  = iSubIndex
              TmpPar.szContents = szContents      .
   
   triggerCall ( methodName, DATASET-HANDLE hDsPar).
   
   /* here comes error handling */
END METHOD.

METHOD PRIVATE LOGICAL triggerCall (methodName AS CHAR, DATASET-HANDLE dsPar):

   dsPar:set-callback("before-fill", methodName, THIS-OBJECT).
   RETURN dsPar:apply-callback("before-fill").   

END METHOD.

And in dialog1.cls we had the different methods that have as input parameter the dataset.
I know it is not a pretty code, but at least we found something for our "reflection" problem in OO.

Regards,
Elod.
 
Back
Top