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

(Sorry for the oddball title... I don't know what else to call it.)

I have got my topmost module that uses several other modules under it's own namespace (so we've got My::Main, My::Main::Child1, My::Main::Child2, etc.). Each of these sub-modules (as well as the topmost module) needs access to a LWP::UserAgent object. What is the best way to accomplish this task of sharing the object? Here is a simple setup of what I am talking about:

=for discussion The Problem: The problem is labelled in My::Main::Child1->usechild(). We wish to access the LWP::UserAgent object, located in the blessed hashref of My::Main::Child1's parent (My::Main is the parent in this case). =cut # My/Main.pm package My::Main; use strict; use LWP::UserAgent; use My::Main::Child1; sub new { my ($class) = @_; my $self = bless( {}, $class ); $self->{_ua} = LWP::UserAgent->new(); return( $self ); } sub newchild { my ($self) = @_; return( My::Main::Child1->new() ); } # My/Main/Child1.pm package My::Main::Child1; use strict; sub new { my ($class) = @_; return( bless( {}, $class ) ); } sub usechild { my ($self) = @_; # PROBLEM LINE! my $res = $self->(...)->{_ua}->get( 'http://www.example.com' ); return( $res->code(), $res->content() ); } # usemain.pl #!/usr/bin/perl -w use strict; use My::Main; my $main = My::Main->new(); my $child = $main->newchild(); my ($code, $content) = $child->usechild();

Below are three (horrible?) solutions I came up with, the first two of which seems okay to me, but I am not sure. The third one works of course, but it causes a lot of headaches if you need the same object instance to be used by all modules -- not to mention the system overhead of creating multiple objects when a single object would do. I think I am leaning heavily towards solution #2, but I do not know if there are any dangers involved.

=for explanation Solution 1: We pass the same LWP::UserAgent object into each child. This way, we don't have extra needless objects created in each child. The mechanics seem only slightly ugly, as we are throwing references all over the place. The major benefit I see is that this will keep object data persistent across all modules (for example, if we pass "agent => 'my special useragent'" to LWP::UserAgent->new() in the topmost module, all child modules will have the same useragent. =cut # My/Main.pm package My::Main; use strict; use LWP::UserAgent; use My::Main::Child1; sub new { my ($class) = @_; my $self = bless( {}, $class ); $self->{_ua} = LWP::UserAgent->new(); return( $self ); } sub newchild { my ($self) = @_; return( My::Main::Child1->new( $self->{_ua} ) ); } # My/Main/Child1.pm package My::Main::Child1; use strict; sub new { my ($class, $ua) = @_; my $self = bless( {}, $class ); $self->{_ua} = $ua; return( $self ); } sub usechild { my ($self) = @_; my $res = $self->{_ua}->get('http://www.example.com'); return( $res->code(), $res->content() ); } =for explanation Solution 2: Similar to solution #1, but instead of passing each object we want to share with the child modules, we pass into each child's constructor our entire self. This way we just shove the entire parent object into a {_parent} key in the child's blessed hashref and then we can access all the parent's data. This appeals to me quite a bit, but it seems careless. Are there any dangers or reasons as to why we would not want to do this? =cut # My/Main.pm package My::Main; use strict; use LWP::UserAgent; use My::Main::Child1; sub new { my ($class) = @_; my $self = bless( {}, $class ); $self->{_ua} = LWP::UserAgent->new(); return( $self ); } sub newchild { my ($self) = @_; return( My::Main::Child1->new( $self ) ); } # My/Main/Child1.pm package My::Main::Child1; use strict; sub new { my ($class, $parent) = @_; my $self = bless( {}, $class ); $self->{_parent} = $parent; return( $self ); } sub usechild { my ($self) = @_; my $res = $self->{_parent}->{_ua}->get( 'http://www.example.com' ); return( $res->code(), $res->content() ); } =for explanation Solution 3: The topmost module and each child create and use their own (independant) object instance. Not only does this create additional needless objects, but since each object instance is completely separate from the others, such a system will fail if we want to use this sort of thing in a situation where data persistence must be kept valid in one object acrosss multiple modules. For example, if we pass "agent => 'my special useragent'" to LWP::UserAgent->new() in the topmost module, all child modules will NOT have the same useragent. We would have to update the topmost module to pass the useragent to each child object, and update each child to pass the useragent to it's own LWP::UserAgent constructor. =cut # My/Main.pm package My::Main; use strict; use LWP::UserAgent; use My::Main::Child1; sub new { my ($class) = @_; my $self = bless( {}, $class ); $self->{_ua} = LWP::UserAgent->new(); return( $self ); } sub newchild { my ($self) = @_; return( My::Main::Child1->new() ); } # My/Main/Child1.pm package My::Main::Child1; use strict; use LWP::UserAgent; sub new { my ($class) = @_; my $self = bless( {}, $class ); $self->{_ua} = LWP::UserAgent->new(); return( $self ); } sub usechild { my ($self) = @_; my $res = $self->{_ua}->get('http://www.example.com'); return( $res->code(), $res->content() ); }

Replies are listed 'Best First'.
Re: Sharing objects between modules
by Corion (Patriarch) on May 05, 2004 at 06:25 UTC

    Others have recommended the Singleton pattern, but I'm not sure if this pattern will suit you in all cases, especially when you want to use different LWP::UserAgent classes (say, LWP::UserAgent and WWW::Mechanize) with different instances of My::Main. If you only need one ::UserAgent subclass per run, then a Singleton is the thing to go with, if you need to switch the class, or supply more than one instance of My::Main with an independent LWP::UserAgent subclass (say, because of cookies), then your way 1 or way 2 are the way to go. Way 3 has the ugly problems you already discussed.

Re: Sharing objects between modules
by Fletch (Bishop) on May 05, 2004 at 01:38 UTC
Re: Sharing objects between modules
by dragonchild (Archbishop) on May 05, 2004 at 02:18 UTC
    You're looking for the Singleton pattern. Each of those objects would instantiate the same class and the class would return the same object. It's pretty easy.

    ------
    We are the carpenters and bricklayers of the Information Age.

    Then there are Damian modules.... *sigh* ... that's not about being less-lazy -- that's about being on some really good drugs -- you know, there is no spoon. - flyingmoose

    I shouldn't have to say this, but any code, unless otherwise stated, is untested

Re: Sharing objects between modules
by pizza_milkshake (Monk) on May 05, 2004 at 02:02 UTC
    sorry, i don't have alot of experience with packages and namespaces in perl so i could be way off or just offering bad advice, but couldn't you just declare whatever you want to share as a package-level variable and then access it by its full-qualified name in other modules?
    #!perl -l use strict; use warnings; package Pizza; $Pizza::foo = "bar"; package Pizza::Milkshake; print $Pizza::foo;
    any enlightment on whether or not this The Wrong Way(tm) are welcome.

    perl -e"\$_=qq/nwdd\x7F^n\x7Flm{{llql0}qs\x14/;s/./chr(ord$&^30)/ge;print"

      That's perfectly fine in many cases; it's as much a singleton as anything else.

      One potential drawback is that it's possible to overwrite a global variable, though. If you use some sort of closure to store the singleton, you'll have less chance of overwriting the variable accidentally.