http://www.perlmonks.org?node_id=198354

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

Is there a way to share an instance of an object with another module?

I have one main program and two modules (A.pm and B.pm). Now i want to share main's instance of module A with module B. Is that possible?

Replies are listed 'Best First'.
Re: single instance shared with module
by sauoq (Abbot) on Sep 16, 2002 at 20:25 UTC
    Now i want to share main's instance of module A with module B. Is that possible?

    Short answer: yes.

    You could pass the object to a sub in B, for example.

    The question is rather vague and general though. Does B just need to act on your instance of A? Does B need to contain your instance of A? What exactly are you trying to do?

    -sauoq
    "My two cents aren't worth a dime.";
    
      main and B both use a method in A (say WriteLog). I thought it'd be a waist to make two instances of A.

      Also, when main instantiates A, it passes a couple of parameters to it that B doesn't (need to) know.

        Well, as I said, you can pass an instance of A (which is an object) to B and B can then call your the object's WriteLog method. You probably would want to pass the instance to B's constructor and allow B to contain the instance of A so it could use it wherever it needed it.

        If A is a class implementing a log file and WriteLog() is a method to write an entry to, say, an open logfile managed by an instance of A and you need to write to the same log file from both the main and B packages, then this approach probably makes sense. Just make sure you document that B uses an instance of A to write to a log.

        Update: Here is some example code:

        #!/usr/bin/perl -w use strict; package A; sub new { my $class = shift; my ($fh) = @_; bless { FH => $fh }; } sub WriteLog { my $self = shift; my $fh = $self->{FH}; print $fh "[",scalar localtime,"] ",@_,"\n" } package B; sub new { my $class = shift; my ($logobj) = @_; bless { A => $logobj }; # B contains instance of A. } sub Foo { my $self = shift; my $log = $self->{A}; $log->WriteLog("B::Foo wrote this!"); } package main; my $A = A->new(\*STDOUT); my $B = B->new($A); # Pass instance of A to B's constructor. $A->WriteLog("main wrote this!"); # main uses instance of A. $B->Foo(); # B also uses it.
        -sauoq
        "My two cents aren't worth a dime.";
        
        Sounds like you might want to pull the WriteLog stuff out into a third class, C. Give the first two classes, A and B, a has-a relationship to the new class C. If there's not too much overhead then A and B can maintain their own instances of C. If there's lots of overhead, then A and B can share a single instance of C by passing the instance reference to the constructors for A and B. Or you can make C a singleton class such that repeated calls to it's constructor yield the same object.

        Matt

Re: single instance shared with module
by jynx (Priest) on Sep 16, 2002 at 21:25 UTC

    Brothers smack me down if i'm wrong, but recently i used something similar to this code to do what you're describing:
    #!/usr/bin/perl -w use strict; package foo; { my $self = { foo => bar }; sub new { return bless $self, shift; } sub get_obj { return UNIVERSAL::isa($self, 'foo') # or not as cool: #return ref($self) eq 'foo' ? $self : goto &new; } }
    This not only makes new instances when you want them, it also can pass on old instances to any class that requires it and uses the 'get_obj' method.

    Whether this is Good Coding(tm) or not, i can't say, but it's worked for me (and i might change what i have, depending on how monks respond to this :)

    jynx

    Update: Added the UNIVERSAL::isa call and commented out the ref($self) call due to podmaster's suggestion. Could someone please elaborate on why UNIVERSAL::isa is better?

    Update2: Whoops. Put the args for UNIVERSAL::isa in wrong order. Hmm, i tested it before i posted and it seemed to work fine the other way as well. Tested again and apparently i was smoking crack. Mental note: RTFM first next time! ;-)

      In brief, UNIVERSAL::isa allows for inheritance, while ref($self) doesn't.

Re: single instance shared with module
by fglock (Vicar) on Sep 16, 2002 at 20:17 UTC

    I guess:

    $obj = A->new; # an object in main $B::a = $obj; # point $B::a to object or: $B::a = \$obj; # $B::a is a ref to object
Re: single instance shared with module
by samurai (Monk) on Sep 16, 2002 at 20:53 UTC
    I believe, if I'm not mistaken, that this is often referred to as a singleton. I believe you can do something like so:

    package A; my $obj = undef; sub new { my $class = shift; unless (ref $obj eq 'A') { # initilize object... $obj = bless \%self, $class; } return $obj; }
    Now both main and B can call the same instance of A by just:

    use A; my $obj = new A();
    ... in theory :) This is UNTESTED code and someone is allowed to thwack me in the back of the head soundly if I am missing something here.

    --
    perl: code of the samurai

      A singleton class makes sense only if you never want more than one instance of the class to exist. If you just need to share an instance, there is nothing stopping you from doing so and allowing multiple instances if you need them.

      -sauoq
      "My two cents aren't worth a dime.";
      

      Be careful when using singletons. They are often a sign that you need to rethink a design. Then can encourage close-coupling of objects and become global variables by another name.

      There is a nice developerworks article on the subject. Even if you can't read Java it's worth a once over.

      That said - logging objects is one of the places that it's probably justified.... so I guess I'll just shutup and go away ;-)

      A singleton is even easier to do in Perl. You don't even need a reference. Because in Perl you can say
      my $class = "Singleton"; $class->method();
      you only need to return a string with the package name in a singleton's constructor.
      package Singleton; my ($various, %instance, @variables); sub new { my $class = shift; # object initialization here ... *new = sub { shift } return $class; } 1;

      Here we also avoid the need to check whether the object is initialized by using a glob to replace &new. None of the further calls to the constructor will execute its initial code.

      All of your methods can just ignore the $self too, since there's a single instance which stores its data in file lexicals.

      Makeshifts last the longest.

      If you do want a singleton you might also want to consider using Class::Singleton. It makes the fact that you want a singleton nice and explicit, and allows sub-classing if necessary.