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.";
| [reply] |
|
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.
| [reply] |
|
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.";
| [reply] [d/l] |
|
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
| [reply] |
|
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! ;-) | [reply] [d/l] |
|
In brief, UNIVERSAL::isa allows for inheritance, while ref($self) doesn't.
| [reply] [d/l] [select] |
Re: single instance shared with module
by fglock (Vicar) on Sep 16, 2002 at 20:17 UTC
|
$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
| [reply] [d/l] |
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 | [reply] [d/l] [select] |
|
-sauoq
"My two cents aren't worth a dime.";
| [reply] |
|
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 ;-)
| [reply] |
|
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. | [reply] [d/l] [select] |
|
| [reply] |