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
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
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 PUBLICvalue.object
foo(bar AS CLASS value.object)
if valid-object(bar) then
bar:GUID = somevalue.
return bar.
END METHOD.
foo(bar1).dosomethingelse().
Urrggghh indeed. Did not know / remember (more likely!) that copies of objects are not deep.
Thanks for the heads up.
Just as a follow-up - Progress does do GC on class objects, right ?
Just as a follow-up - Progress does do GC on class objects, right ?
Correct.
-- peter
Just as a follow-up - Progress does do GC on class objects, right ?
Sure.
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.
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.
for once, I agree with a Doctor's prognosis
Julian
On 20 May 2010 17:30, Thomas Mercer-Hursh
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.