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

Over the last few months I have set about converting one of my largest projects into Perl Objects and have been thrilled by the functionality perl's objects provide (not to mention the performance boost in mod_perl).

My question however relates to passing objects to other objects. So far I've been creating objects then passing them to other objects in the constructors e.g.
my $object1 = new MyClasses::Object1 ( _cgi => new CGI, param1 => '...', param2 => '...', ); $object1->dothis; my $object2 = new MyClasses::Object2 ( _object1 => $object1, param1 => '...', param2 => '...' ); $object2->setsomething; my $object3 = new MyClasses::Object3 ( _object1 => $object1 ); $object3->blah;
The way this works I can declare all my parameters for respective objects and reuse them again without wasting new declarations, and modify them between different objects, e.g.
Inside Object2: sub setsomething { my $self = shift; $self->{_object1}->{mode} = 1; } Inside Object3: sub blah { my $self = shift; if ($self->{_object1}->{mode}){ return $self->{_object1}->{_cgi}->param('blah'); } else { return $self->{_object1}->{_cgi}->param('pleh'); } }
After looking at a bunch of modules and other perl code I have realised I have made an error by making object argument names _object seeing that is is against the private variable declaration (however I really like my convention seeing that I can tell an object and a normal argument apart).

I have also been unable to find any other code that uses object passing like this which brings be to my question:

How, as a proper perl programmer using proper conventions should this be written? I have not seen any object passing in other CPAN modules or example code so how should it be done?

I realise that what I have done works and for me works very well but knowing the correct way to do it is very important for me for future reference (or rewriting if I've completly stuffed it up) =).

Thanks,
James.

P.S. Hope you can understand what I'm trying to ask, It's very early and I'm almost out of battery =)

Replies are listed 'Best First'.
Re: Conventions with Passing Objects to Objects
by diotalevi (Canon) on Jan 13, 2006 at 14:43 UTC

    This note is about some bad syntax on your part. I see you're using the new Some::Object syntax. That's bad. Don't. This syntax is really fragile and can cause your program to be parsed very differently depending on compilation order and whether you're writing an OO module yourself. If you switch to the Some::Object->new( ... ) syntax, it will always be unambiguous and the right thing will happen in all cases.

    The following snippet highlights the most common case that will go completely wrong.

    package Foo; sub new { ... } sub bar { ... = new Some::Object( ... ); # You meant # ... = Some::Object->new( ... ); # but you got # ... = Foo::new( "Some::Object", ... ) }

    ⠤⠤ ⠙⠊⠕⠞⠁⠇⠑⠧⠊

      Thanks for your help, that thread on delegation vs inheritance helps tremendosly and sorts alot of stuff out in my head.

      Also thanks heaps to diotalevi, I've heard about the unambiguity with new before but not understood the problems, that's the first time anyones actually illustrated the problems clearly.

      Looks like I better write a perl script to parse my scripts and correct that syntax. ;)

      Thanks again,
      James.

        B::Deparse will do that. perltidy will do that even better.

        ⠤⠤ ⠙⠊⠕⠞⠁⠇⠑⠧⠊

Re: Conventions with Passing Objects to Objects
by xdg (Monsignor) on Jan 13, 2006 at 14:18 UTC

    Without more details of what you're actually doing with these, I don't see anything particularly wrong with it. Handing an object as a parameter to another object is just fine -- things of that sort are frequently refered to as following a "delegation" pattern -- though that does have some specific semantics. Some useful references include:

    Underscore parameters (and subs) have the convention of indicating private, but there's no real reason you can't deviate as needed. If it helps you in your coding, but you want to be more consistent for users, you might take "object1" as an argument to new but store it as _object1 internally to help distinguish in your coding.

    -xdg

    Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

Re: Conventions with Passing Objects to Objects
by derby (Abbot) on Jan 13, 2006 at 14:20 UTC

    I have realised I have made an error by making object argument names _object seeing that is is against the local variable declaration

    Maybe I'm dense today but I don't comprehend this statement. I see no problem with naming your object fields _foo. As long as _foo is descriptive of what the object is. About the only thing you run into is the perl convention that variable names starting with _ denotes a private variable that should not be modified

    Personally I do note try to put anything in a name to denote it's type (I was scarred by early MS hungarian notation). I much prefer a descriptive name that tells me what the variable is in an overall sense than in any type of type sense (hence cgi is better than _cgi for me since I pretty much know, by convention, that cgi is going to be sometype of CGI object).

    -derby
Re: Conventions with Passing Objects to Objects
by jhourcle (Prior) on Jan 14, 2006 at 04:42 UTC
    After looking at a bunch of modules and other perl code I have realised I have made an error by making object argument names _object seeing that is is against the private variable declaration (however I really like my convention seeing that I can tell an object and a normal argument apart).

    The first rule of coding -- write for whomever is going to be maintaining the code. In a case like this, it's you, not someone else, so you are free to use whatever coventions you like. There's no one right way to do it -- I just suggest that whatever convention you use, you try to use it as consistently as possible.

    I have also been unable to find any other code that uses object passing like this which brings be to my question:

    How, as a proper perl programmer using proper conventions should this be written? I have not seen any object passing in other CPAN modules or example code so how should it be done?

    If you want to jump right in, one example of Perl that uses a bunch of objects that reference other objects, see SOAP::Lite. I haven't done a full read of it since 0.55, but I found it enlightening, and disturbing at the same time.

    I realise that what I have done works and for me works very well but knowing the correct way to do it is very important for me for future reference (or rewriting if I've completly stuffed it up) =).

    If you have a system that works, there's little reason to change just for change's sake. If you just want to bone up on what's 'normal' for Perl (if there is such a thing), I'd suggest reading Perl Best Practices and Perl Medic

Re: Conventions with Passing Objects to Objects
by Anonymous Monk on Jan 17, 2006 at 17:03 UTC
    In general it's fine to store one object inside another as you are doing. However, there is an issue with stuff like this: Quote:
    Inside Object2: sub setsomething { my $self = shift; $self->{_object1}->{mode} = 1; }
    What this means is that you have given Object2 some "inside knowledge" of how Object1 is implemented, because Object2 is "reaching inside" Object1's hash to get the "mode" key. This is considered bad. You, or someone else, might decide to change the implementation of Object1 -- maybe you'll decide to call "mode" something else, or even that Object1 should use an arrayref instead of a hashref for performance reasons. Then Object2 will break. Clean object encapsulation requires that you hide implementation details from other objects, and present only an API for them to use. What this means is you have to provide an accessor function in Object1 like this:
    sub mode { my $self = shift; $self->{mode} = $_[0] if (@_); $self->{mode}; }
    And then in Object2, instead of
    $self->{_object1}->{mode} = 1;
    you use
    $self->{_object1}->mode(1);
    The main advantages: a) You are free to change the internal implementation of Object1 without affecting Object2. b) You can choose to perform additional tasks in Object1 when mode is set, or read. Even choose to deny access. Writing accessor functions by hand is tedious though, so it's good to use something like Class::Accessor, or weave some magic with AUTOLOAD and a _permitted list.
      Thanks for pointing this out, I've noticed that I have recently started falling into this trap as I've become more experienced with Objects. I will make sure I stear clear of it in the future. Thanks, James.