input-output of an object

Posted by jmls on 20-May-2010 07:10

Let's say I have a value object "bar"

this object needs to be passed into a method in order to have a unique ID allocated b some business rule. This object is then used elsewhere.

so, would it be better to say

METHOD PUBLIC VOID foo(INPUT-OUTPUT bar AS CLASS value.object)

... [snip]...

      bar:GUID = somevalue.

... [snip]...

END METHOD.

foo(input-output bar1).

or

METHOD PUBLIC value.object foo(bar AS CLASS value.object)

     DEF VAR mybar AS CLASS value.object NO-UNDO.

... [snip]...

     mybar = bar.

     mybar:GUID = somevalue.

     DELETE OBJECT bar.

     RETURN myvar.

... [snip]...

END METHOD.

bar1 = foo(bar1)

for some reason, I've never liked INPUT-OUTPUT  - don't ask, because I can't tell you because I have no idea - but I can see the advantage in this scenario. However, I like the 2nd option more, as I am not fiddling with the input data.

Julian

All Replies

Posted by Peter Judge on 20-May-2010 08:24

Let's say I have a value object "bar"

this object needs to be passed into a method in order to have a unique ID allocated b some business rule. This object is then used elsewhere.

so, would it be better to say

for some reason, I've never liked INPUT-OUTPUT  - don't ask, because I can't tell you because I have no idea - but I can see the advantage in this scenario. However, I like the 2nd option more, as I am not fiddling with the input data.

You could simply not pass anything back, since objects are passed by reference and so you're modifying the passed-in object.

METHOD PUBLIC VOID foo(bar AS CLASS value.object)
     DEF VAR mybar AS CLASS value.object NO-UNDO.

... [snip]...


     mybar = bar.
     mybar:GUID = somevalue.

     DELETE OBJECT bar.
     RETURN myvar.

... [snip]...
END METHOD.

[update] I was about to remove the RETURN statement when I saw that you're assigning "bar" to "mybar". Obviously, if you're doing so, then you should return value.object and not VOID, but if you're only doing so to return "mybar" then you might want to look at why you're doing that.

-- peter

Message was edited by: Peter Judge

Posted by Matt Baker on 20-May-2010 08:38



METHOD PUBLIC value.object foo(bar AS CLASS value.object)
     DEF VAR mybar AS CLASS value.object NO-UNDO.

... [snip]...


     mybar = bar.
     mybar:GUID = somevalue.

     DELETE OBJECT bar.
     RETURN myvar.

... [snip]...
END METHOD.

bar1 = foo(bar1)

EEK!!  Doing this:

mybar = bar.


Creates two pointers to the same object.  If you follow it with this:

DELETE OBJECT bar.

You have just deleted your object and ALL references to it are invalid.

Objects are not deep copy.  They are always pass by reference.

So you simply need this:

METHOD PUBLIC void foo(bar AS CLASS value.object)
     if valid-object(bar) then
          bar:GUID = somevalue.
END METHOD.

foo(bar1)

Or if you like to chain references do this, but there is no need to have an intermediate variable.  There are some languages which explicitly return a copy of the "this" if the method that is invoked on the object returns void.  ABL doesn't but you can certainly do it in your code.  Just make sure you note the behavior on the comments.


/*
  Assigns a guid to the bar object and returns the bar object iself.
*/
METHOD PUBLIC
value.object foo(bar AS CLASS value.object)
     if valid-object(bar) then
          bar:GUID = somevalue.
     return bar.
END METHOD.

foo(bar1).dosomethingelse().

Posted by jmls on 20-May-2010 10:04

Urrggghh indeed. Did not know / remember (more likely!) that copies of objects are not deep.

Thanks for the heads up.

Posted by jmls on 20-May-2010 10:07

Just as a follow-up - Progress does do GC on class objects, right ?

Posted by Peter Judge on 20-May-2010 10:28

Just as a follow-up - Progress does do GC on class objects, right ?

Correct.

-- peter

Posted by Admin on 20-May-2010 10:28

Just as a follow-up - Progress does do GC on class objects, right ?

Sure.

Posted by Matt Baker on 20-May-2010 10:29

Yes it does GC.  It is very simple and (currently) based on reference counting. It can leave stuff in memory if you have circular references.  The biggest benefit it gives you is that any object whose lifetime is only the span of a single function (e.g. not a class member) you don't usually don't have to worry about it.

Example if you have references to objects like this (it makes more sense as a picture..but I can't draw with a text editor):

a:b = b

b:c = c

c:a = a

d:a = a

and you set d:a = ? (unknown/null) then a b and c can still stay in memory after a garbage collection since they have live references to each other in the form of a loop.  a, b, and c are not accessible from any other code.

You have to break the loop somewhere in your code.  You do this by explicitly deleting one of the objects in the chain, or setting its "next" reference to unknown.

instead of d:a = ? you should actually do this:

def var a as .

a = d:a.

d:a = ?.

delete object a.

...

return

or

def var a as .

a = d:a.

d:a = ?.

a:b = ?.

...

return.

or

If object "d" always needs a reference to "a", then you can just call "delete object a". in the destructor for "d" which would also take care of it.

Posted by Thomas Mercer-Hursh on 20-May-2010 11:30

For the record, I would vote for the void version since the return version might create the impression that something was actually being returned.  Better to get the notion of pass by reference clearly in one's mind because anything else is going to lead to problems like mybar = bar.

Posted by jmls on 20-May-2010 13:03

for once, I agree with a Doctor's prognosis

Julian

On 20 May 2010 17:30, Thomas Mercer-Hursh

Posted by Thomas Mercer-Hursh on 20-May-2010 13:19

I might also note that one expression of this principle is that one generally does not want to pass object references with behavioral method calls.  Rather, the expectation is that the object with the behavioral method knows how to navigate to the object it needs.  Obviously, at some point that object needs to obtain a reference to the object on which the behavior is going to occur, but obtaining that reference and acting on it should often be two separate acts, the former done only once.

This thread is closed