newbio has asked for the wisdom of the Perl Monks concerning the following question:

Hi Monks,

Please advise on this.

package ABC; { sub new { my $hashref={}; my @array=(); my $arrayref=[]; $class=$_; bless ($hashref,$class); bless (@array,$class); bless ($arrayref,$class); return $hashref, @array, $arrayref; #I think I can only return one of these three data structures but not +all, right? } sub get { #To access $hashref, @array, and $arrayref } sub set { #To set $hashref, @array, and $arrayref } } 1; #main $object1=ABC->new(); #assuming ABC class returns $hashref $object2=ABC->new(); #assuming ABC class returns @array $object3=ABC->new(); #assuming ABC class returns $arrayref

Basically, I want to create a class ABC with three separate data structures, $hashref, @array, and $arrayref and I want some mechanism to manipulate the data in them. Questions are,

Q1. How do I make the constructor return all three data structures, $hashref, @array, and $arrayref when invoked. Is it possible? I understand that only one of these three can be returned by the constructor and not all together. Correct?

Q2. I guess with $object1,$object2,$object3, I am creating three different objects. Right? Is there a way by which I create just one object of ABC and through this object I am able to manipulate data in all three data structures of, $hashref, @array, and $arrayref.

Q3. Do I need to create three different sets of get() and set() functions each to manipulate $hashref, @array, and $arrayref respectively by creating their objects first, ie $object1,$object2,$object3 ?

Q4. I understand I can manipulate $hashref, @array, and $arrayref only through access functions like get() and set() and not directly like $object1->$hashref or something like it, am I correct?

Q5. Suppose I create a nested data structure reference in the constructor, say $all={}, that contains all three, $hashref, @array, and $arrayref and if I bless $all with class ABC, then I can create just one object which I can then use to manipulate the data contained in $hashref, @array, and $arrayref. Also, this way I may just need one set of get()/set(). Is this a feasible solution? Would a nested data structure of $hashref, @array, and $arrayref together be better solution than their three separate data structures? Perhaps, the nested structure might make the program slower, yes/no?

I am not sure if I am making sense. Am I missing something? Please let me have your comments guys. What are the common ways of doing such things? Please guide.

Thanks a lot!

Cheers, Raj

Replies are listed 'Best First'.
Re: On Perl Objects!
by Golo (Friar) on Oct 24, 2004 at 13:12 UTC
    Q1. How do I make the constructor return all three data structures, $hashref, @array, and $arrayref when invoked. Is it possible? I understand that only one of these three can be returned by the constructor and not all together. Correct?
    A constructor is ment to return a reference to the just created instance of your class, which is of type "ABC".
    Q2. I guess with $object1,$object2,$object3, I am creating three different objects. Right? Is there a way by which I create just one object of ABC and through this object I am able to manipulate data in all three data structures of, $hashref, @array, and $arrayref.
    Yes, your object will hold those three data structures and provide access to them using methods (the oo term for funtions).
    Q3. Do I need to create three different sets of get() and set() functions each to manipulate $hashref, @array, and $arrayref respectively by creating their objects first, ie $object1,$object2,$object3 ?
    You could use the functions to manipulate your data structures through them instead of only retrieving them to set them again later. If you don't want / need this, you probably don't need OO for this task at all.
    Q4. I understand I can manipulate $hashref, @array, and $arrayref only through access functions like get() and set() and not directly like $object1->$hashref or something like it, am I correct?
    You should only access your data through methods. One idea/goal behind OO is to encapsulate your data. Simple accessors/setters/getters may just look like needless typing work, but imagine you want to change the way your data is stored at a later point. By using the encapsulation you can do so without having to change any code that is using your class (could be a lot!) by only altering your class.
    Q5. (...) Perhaps, the nested structure might make the program slower, yes/no?
    See Answer to Q2 and Q3 for the first part of your question. For program speed you should be aware that OO will most likely have a negative impact. The main purpose why to use OO is structure, maintainability, code reuse and the like over program speed.

    Hope I could provide some useful hints.
Re: On Perl Objects!
by Wonko the sane (Curate) on Oct 24, 2004 at 13:23 UTC
    You may want to try using something like a Blessed subroutine as your object.
    If you team this with your own AUTOLOAD subrotine, you can really get alot of flexibility
    Not only does this method offer you a way to privately encapsulate
    your objects internal data structure, but gives you the flexibility of doing some really
    cool things with the way the data is stored or retrieved.

    The subroutine can have whatever logic you want coded into it.
    You can have multiple encapsulated private data objects (array, hashes, scalars etc),
    and even build in permissions or validation logic to certain actions or attributes.
    The AUTOLOAD routine can be coded to handle most of the simple accessors, saving
    you the trouble of coding them all, and your script from compiling them all each run.

    This also helps force people to access your object through the accessor methods rather than
    getting at the data itself, like what happens with blessed hashes or arrays.

    package Bill; use strict; use warnings; our ( $AUTOLOAD ); sub AUTOLOAD { my ( $self, $newval ) = @_; no strict q{refs}; if( $AUTOLOAD =~ /.*::get_(.+)/ ) { my $attr = $1; # Class Attribute name. *{$AUTOLOAD} = sub { $_[0]->( q{get}, $attr ) }; #cache return $self->( q{get}, $attr ); } elsif ( $AUTOLOAD =~ /.*::set_(.+)/ ) { my $attr = $1; *{$AUTOLOAD} = sub { $_[0]->( q{set}, $attr, $_[1] ) }; #cache return $self->( q{set}, $attr, $newval ); } } sub new { my ( $class, $args ) = @_; my %attribs; # Hold and ecapsulate all object attribs. my @protected_attribs = qw( userid access_level ); # This means that rather than being able to get at object # attribs like this... # $self->{obj_attrib} # you have to do this... # $self->( 'get', 'obj_attrib' ); # $self->( 'set', 'obj_attrib', 'new_value' ); # OR through use of the AUTOLOAD routine... # $self->get_obj_attrib; # $self->set_obj_attrib( 'new_value' ); my $self = sub { my ( $cmd, $attr, $newval ) = @_; if ( $cmd eq q{set} ) { unless ( scalar grep { $attr eq $_ } @protected_attribs ) { $attribs{$attr} = $newval } } return $attribs{$attr}; }; $self = bless $self, $class; #----------------------------------------- # Do any initialization that is needed for the objects data $attribs{dbh} = $args->{dbh}; return wantarray ? ( 1, $self ) : $self; } # END new

    Damian Conway's Object Oriented Perl book, has more on this type of object structure.
    One of the best Perl books I have ever read.

    Updated: Added link for Damian's book.

    Hope that helps,
    Wonko

Re: On Perl Objects!
by gaal (Parson) on Oct 24, 2004 at 15:16 UTC
    A constructor is something that allocates a new object and returns a reference to it. If your "new" returns three (references to) objects, it is more like a factory, although since it didn't delegate the actual construction to anyone, it also includes the constructor. Confusing? Good, because this is not how you'd usually want to do this. :-)

    As have already figured out in your Q5, what you usually want to do is create something that contains three objects. Very typically this is a hash in Perl, especially if each element object fulfills a role. So Human->new() may call Arm->new() and Leg->new() twice each; and put the resulting members in its fresh new $self->{right_arm}, $self->{left_arm}, $self->{right_leg}, and $self->{left_leg}.

    You would then access the nested objects via indirection, which is a bit slower but not significantly so for most purposes. For example:

    $person = Human->new(); $person->{right_arm}->grasp("banana"); # not through accessor $person->get_right_arm->grasp("banana"); # through accessor # or, you might write accessors like this too if you think they'd be u +seful: $person->arm("left")->grasp("orange");

    What you should write depends on what you really need. What do your objects represent?

Re: On Perl Objects!
by dws (Chancellor) on Oct 25, 2004 at 03:28 UTC

    What do you expect

    $class=$_;
    to do? Where is $_ getting set?

    I think you mean to write

    my $class = shift;
    since the package name (e.g., "ABC") get passed as the first argument when you write
    ABC->new();

Re: On Perl Objects!
by TedPride (Priest) on Oct 25, 2004 at 02:38 UTC
    You can return as many structures as you want:
    my ($hashref, $arrayref, @array) = new(); print $hashref->{'a'}; print $array[0]; print $arrayref->[1]; sub new { my $hashref = {'a' => '1'}; my @array = (2,3); my $arrayref = [4,5]; return ($hashref, $arrayref, @array); }
    You just have to be careful how you return them. An array is returned as a list of items, for instance, and Perl can't figure out where the list ends if you don't pass the array last:
    ## $arrayref isn't defined! my ($hashref, @array, $arrayref) = new(); print $hashref->{'a'}; print $array[0]; print $arrayref->[1]; sub new { my $hashref = {'a' => '1'}; my @array = (2,3); my $arrayref = [4,5]; return ($hashref, @array, $arrayref); }
    For this reason, it's generally safer to pass arrays only via reference. Passing via reference is more efficient anyway, especially when working with large arrays.
Re: On Perl Objects!
by newbio (Beadle) on Oct 26, 2004 at 13:37 UTC
    Thanks a lot guys! Cheers, Raj