Object instanciation and properties

Posted by Gabriel Hautclocq on 22-Dec-2014 17:18

Hello,

I have performance issues with my ABL classes (Progress 10.2b).

Are there some best practice tips for Object properties ?

I try to use lazy instantiation on my properties to avoid infinite loops caused by circular references, but I think the properties are evaluated during the object instantiation, defeating the benefits of using lazy instantiation.

Here is an example:

DEFINE PRIVATE VARIABLE cl_enterprise AS NameSpace.Enterprise NO-UNDO.

DEFINE PUBLIC PROPERTY pin_EntId AS INTEGER NO-UNDO
	GET.
	PRIVATE SET.


DEFINE PUBLIC PROPERTY pcl_Enterprise AS NameSpace.Entreprise NO-UNDO
		GET():
				IF NOT VALID-OBJECT( cl_enterprise )
					 AND pin_EntId > 0
				THEN DO :
						cl_enterprise = NEW NameSpace.Enterprise( pin_EntId ).
				END.
				RETURN cl_entreprise.
		END GET.
		PRIVATE SET( INPUT pr_cl_valeur AS NameSpace.Entreprise ):
				THIS-OBJECT:cl_enterprise = pr_cl_valeur.
		END SET.

CONSTRUCTOR PUBLIC Contact(
		INPUT pr_in_EntId AS INTEGER ):

		SUPER().

		ASSIGN pin_EntID   = pr_in_EntId.
END CONSTRUCTOR.


I'm trying to instantiate the object cl_enterprise on first access. Am I doing something wrong ?

All Replies

Posted by Tim Kuehn on 22-Dec-2014 17:24

Lazy instantiation is to delay creating a class until it's actually needed - it won't solve problems like unintended circular references and their related issues.

Looking at your code, I'd just make the "SET" private and not have any code in it, since pcl_Enterprise is never referred to directly in this code sample.

Beyond that - have you tried running this using the debugger to see where the execution path is actually going?

Posted by Gabriel Hautclocq on 22-Dec-2014 17:36

Thanks for your reply.

This is a code sample, I actually need to keep the code in the private set.

I try to do some lazy instantiation so that when an object is instantiated, its object properties aren't until accessed. But as the properties get evaluated during the execution of the constructor, the lazy instantiation does not work.

About the circular reference issue:

Say we have a class Company with a property Director, and a class Director with a property Company.

To me it seems natural to do this way.

However there is a circular reference, and it's dangerous (for example for serialization) which leads me to think that there must be a better way to do this without a circular reference. I just can't find how.

Any idea?

Posted by Gabriel Hautclocq on 22-Dec-2014 17:52

I did a few tests. On Windows properties don't get evaluated during the instantiation. But on Unix they are. Weird!

Maybe this has to do with compilation. On Unix my source files aren't compiled, but on Windows they are automatically compiled.

Posted by Tim Kuehn on 22-Dec-2014 17:52

Then I'm confused, as I'd expect you to be setting pcl_enterprise in the SETter, not cl_enterprise. If you're setting cl_enterprise in the setter, then the object reference in the GETter will be lost when the SETter is called.

As for the circular reference, I'd surmise you need to split the "company" and "director" classes up so they're not referencing each other that way and then use a container class to hold both of them.  Usually I'd look at the more general case (Company) would hold the less general case (Director). If the director class needs to know which company the director belongs to, then pass the Company reference to the Director's constructor.

Posted by Tim Kuehn on 22-Dec-2014 17:53

do a full recompile of the project on both - if you have different behavior between the platforms, then that's a bug and needs to be reported.

Posted by Gabriel Hautclocq on 22-Dec-2014 17:58

Thanks. That makes sense.

As for why I use a private variable for the Get/Set, that's because once I got a Get loop when I do the following:

DEFINE PUBLIC PROPERTY pA AS test.A
        GET():
            IF NOT VALID-OBJECT ( pA )
                THEN DO :
                pA = NEW test.A().
            END.
            RETURN pA.
        END GET.
        PRIVATE SET.

As "pA" is self-referenced inside the getter itself, there was a loop (in the "IF NOT VALID-OBJECT( pA )" line).

Posted by Tim Kuehn on 22-Dec-2014 18:22

This is what I do to implement delayed instantiation:

USING Answers.DelayedInstantiation.*    FROM PROPATH.

CLASS Answers.DelayedInstantiation.DelayedInstantiationParent: 

DEFINE PUBLIC PROPERTY DelayedChild  AS DelayedInstantiationChild   NO-UNDO 
                                    GET:    IF NOT VALID-OBJECT(THIS-OBJECT:DelayedChild)  THEN 
                                                DelayedChild = NEW DelayedInstantiationChild().
                                            RETURN(THIS-OBJECT:DelayedChild).
                                    END GET.  
                                    PRIVATE SET. 

END CLASS.
If you're getting an infinite loop, then something else is going on in your code. 

Posted by Marian Edu on 23-Dec-2014 01:02

don't see anything wrong with that, I  would just skip the setter on the property as that it's just a proxy to the private variable keeping the actual reference

using a private variable to keep the reference is mostly just to avoid useless object creation on destructor if it happens not to be instantiated by that time as will happen in Tim version, aka create something when actually trying to clean-up.

other than that I've never seen a behavior where properties are evaluated on constructor unless some piece of code fired by the constructor will access those properties so you better check out what is happening in those constructors, your class and base class(es)

Posted by Tim Kuehn on 23-Dec-2014 12:10

Actually, thinking about this, the OP needs to create a factory that'll create both the Director and the Company objects and then pass the appropriate company / director object to the object being created for it to use.

This thread is closed