If we asign an object variable to other object variable in pl/sql, the object is cloned, because pl/sql doesn't work with references. For example, the following code will print two different sentences:
create or replace type cla as object -- class (would be very complex)
(
name varchar2(50)
);
declare
o1 cla;
o2 cla;
begin
o1 := cla('hi cloning world');
o2 := o1;
o1.name = 'goodbye cloning world';
dbms_output.put_line('o1.name: ' || o1.name);
dbms_output.put_line('o2.name: ' || o2.name);
end;
So I thought that I could encapsulate the object into other object (external object), and if I assign the external object to other external object, then the internal object wouldn't be cloned:
create or replace type cla as object -- class (would be very complex)
(
name varchar2(10)
);
create or replace type eo_c as object -- class to encapsulate objects
(
o cla -- encapsulation is easy
);
declare
eo eo_c;
eo2 eo_c;
begin
eo := eo_c( cla('n1') ); -- eo is easy to create
dbms_output.put_line('eo.name: ' || eo.o.name); -- real object is easy to access
eo2 := eo; -- eo_c object is cloned, cla object shouldn't be cloned
eo.o.name := 'n2'; -- if eo.o == eo2.o then we are changing both names
dbms_output.put_line('eo.name: ' || eo.o.name);
dbms_output.put_line('eo2 name: ' || eo2.o.name);
end;
But that prints two different sentences again, so the internal object is cloned too.
Can I encapsulate the object into some other type of variable to avoid the cloning of the internal object? Or more generally, is there some trick to avoid the cloning of the object, and at the same time allow an easy way to work with it?
Based on Alex suggestion (use an associative array), I have created a package that encapsulates objects, so we can use them in an abstract way, as if they were references:
create or replace type cla as object -- complex class
(
name varchar2(10)
);
create or replace package eo as -- package to encapsulate objects
type ao_t -- type for hash (associative array)
is table of cla
index by varchar2(100);
o ao_t; -- hash of objects
end;
declare
o1 varchar2(100);
o2 varchar2(100);
begin
o1 := 'o1'; -- objects are hash indexes now
eo.o(o1) := new cla('hi'); -- store new object into the hash
o2 := o1; -- assign object == assign index
eo.o(o1).name := 'bye'; -- change object attribute
dbms_output.put_line('eo.o(o1).name: ' || eo.o(o1).name);
dbms_output.put_line('eo.o(o2).name: ' || eo.o(o2).name); -- equal?
end;
Now 'bye' is written twice, as expected with object references. The trick is that both o1 and o2 contain the same index (~reference) to the same object. The syntax is a bit more complex, but still very similar to standard object manipulation when accessing both attributes and methods.
Assigning an object to other is exactly as standard object assigning:
o2 := o1;
Same for using an object as a function argument:
afunc(o1);
Internally, afunc() will just use o1 with the same special syntax to access methods or attributes (and no special syntax to assign):
eo.o(o1).attrib := 5;
eo.o(o1).method('nice');
o3 := o1;
The only requirement to use this trick is to add a hash (type and variable) to the eo package for each class we want to encapsulate.
Update: The index value based on the variable name:
o1 := 'o1';
could be a problem if, for example, we create the object in a funcion, since the function would have to know all values used in the rest of the program in order to avoid repeating a value. A solution is to take the value from the hash size:
o1 := eo.o.count;
That takes us into other problem: The hash content is persitent (since it is into a package), so more and more objects will be added to the hash as we create objects (even if the objects are created by the same function). A solution is to remove the object from the hash when we are done with the object:
eo.o(o1) = null;
So the fixed program would be:
create or replace type cla as object -- complex class
(
name varchar2(10)
);
create or replace package eo as -- package to encapsulate objects
type ao_t -- type for hash (associative array)
is table of cla
index by varchar2(100);
o ao_t; -- hash of objects
end;
declare
o1 varchar2(100);
o2 varchar2(100);
begin
o1 := eo.o.count; -- index based on hash size
eo.o(o1) := new cla('hi'); -- store new object into the hash
o2 := o1; -- assign object == assign index
eo.o(o1).name := 'bye'; -- change object attribute
dbms_output.put_line('eo.o(o1).name: ' || eo.o(o1).name);
dbms_output.put_line('eo.o(o2).name: ' || eo.o(o2).name); -- equal?
eo.o(o1) = null; -- remove object
eo.o(o2) = null; -- remove object (redundant)
end;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With