Re: Avoiding global object handle
by jeffenstein (Hermit) on Mar 26, 2002 at 12:03 UTC
|
You could also take a look at Class::Singleton. Possibly you could subclass this to be a repository for handles. For instance, (assuming $rep is an instance of a subclass of Class::Singleton) add a handle using $rep->add_handle("Module1", Module1->new() );, then call $rep->get_handle("Module1") to retrieve the handle later. Of course, you'll need to write these methods yourself.
That way, you only need to add use Rep; our $rep = Rep->new(); into the modules that will need to access any of the handles.
It has the same effect of having globals, but just seems to me a bit cleaner, along with less names of globals running around.
| [reply] [d/l] [select] |
Re: Avoiding global object handle
by Smylers (Pilgrim) on Mar 26, 2002 at 10:30 UTC
|
You dismissed method 2 as possibly being too much of a performance hit, since it involves passing a scalar variable containing a reference into each subroutine.
But your preferred method involves calling an extra subroutine every time you want to access your object. I haven't done any tests, but I'd be surprised if sticking an extra param in an existing sub call is more costly than making a whole new call. Especially if in a particular sub you do ten method calls on your object — you've now replaced one param with ten function calls.
Did you actually have a problem with your code running too slow, or did you just get the urge to do this because you suspected there might be one?
| [reply] |
|
If I have to call the get_handle() when I need it, I can appreciate that there's a performance cost. But pulling this handle through numerous calls to subs that don't need the handle themself, with the only reason to provide the handle to a 'sub-sub' feels as paying for something without getting anything in return.
Furthermore, imagine three subs named x(), y() and z(). sub x() calls sub y() that calls sub z().
One day sub z() is changed and needs access to the handle. As a result, the programmer must change sub x() and sub y() to take the handle as a parameter even though they couldn't care less about the handle themself.
This would lead to discussions like: "If we want to change sub z() to use the functionality of Module.pm, then we must first do an impact analyze to determine how many other subs must be enhanced to provide the handle, directly or indirectly, to our sub z()".
If sub z() can get a direct access to the handle, the change can be implemented in sub z() with no impact on other subs.
Yes, I'm talking about rather big and complex Perl applications here.
Everything went worng, just as foreseen.
| [reply] |
|
"Especially if in a particular sub you do ten method calls on your object — you've now replaced one param with ten function calls."
Not necessarily. If I know that I'm going to use the object 10 times in a specific sub, I can call get_handle() once and save the handle in my own variable throughout the sub.
sub z {
my $handle=get_handle();
$handle->method_a('parameter');
$handle->method_b('parameter');
$handle->method_c('parameter');
# Etc.
}
Everything went worng, just as foreseen.
| [reply] [d/l] |
Re: Avoiding global object handle
by Jammer (Novice) on Mar 26, 2002 at 12:41 UTC
|
According to "Design Patterns" (Gamma/Helm/Johnson/Vlissides), what you describe is a Singleton, as is your implementation of it.
This is the OO "correct" (whatever that means ;) way of ensuring a class has only one instance, and of providing a global point of access to it.
I usually call the get_handle() method get_instance() when I do this, so I know what it is when I come back to it later.
| [reply] [d/l] [select] |
Re: Avoiding global object handle
by Anonymous Monk on Mar 26, 2002 at 13:25 UTC
|
I don't quite see the point in having a global function instead of a global variable. To me it makes more sense to have a global variable since that needs to be declared. If you have a subroutine that you think you can remove you won't find out that it perhaps was needed until the program dies because the subroutine was missing in some obscure corner of the program. However, you'll instantly know if the variable is missing. Furthermore, if you use our() you'll get a warning if you don't use the variable.
Example:
sub make_handle {
...
}
our $handle = make_handle(); # "... used only once"
So I consider globals a good thing when they're proper to use. One can argue that using a variable like this (and still keep the 'used only once' warning) will require you to always setup the handle. But if it's a large application and the handle will almost always be used then I see no reason not to initialize it right away.
I also believe it's faster to use a variable than to perform a sub call. But if you still want to use a sub call and really really care about performance, you might want to try a hack like this:
sub foo {
my $foo = shift;
return undef unless defined $foo;
no warnings 'redefine';
*foo = sub { $foo };
foo();
}
{
my $bar;
sub bar {
defined $bar ? $bar : ($bar = shift);
}
}
&foo and &bar are analogous. But &foo runs faster, at least according my benchmark.
Cheers,
-Anomo | [reply] [d/l] [select] |
|
I don't quite see the point in having a global function
instead of a global variable. To me it makes more sense to
have a global variable since that needs to be declared.
Accessing the global handle through a subroutine makes
it much harder to accidentally clobber it. (Further, it
makes clear to anyone reading your code that
$handle has a well-defined value that doesn't
change, which is kind of nice for maintenance programmers.)
In general, I'm a big fan of wrapping related
scope-specific variables in a singleton class, just because
it makes domain control so much easier. This works best in
very specific cases, where you don't see much potential for
partial code reuse, because you're coupling all the code
that depends on the singleton. It becomes correspondingly
harder to pull out individual functions from that scope and
use them elsewhere, because you have to pull out the
singleton as well (which probably doesn't fit your new
problem perfectly). In Biker's situation, this might be
a good fit.
--
:wq
| [reply] [d/l] |
|
I'm speaking, of course, of when it's best fit to use a variable/subroutine instead of some other technique.
Accessing the global handle through a subroutine makes it much harder to accidentally clobber it.
True. I didn't think of that, especially since he uses it as foo()->bar, and it's hard to modify it that way. I don't think there's a high risk of accidently modifying this particular variable, but I see your objection.
(Further, it makes clear to anyone reading your code that $handle has a well-defined value that doesn't change, which is kind of nice for maintenance programmers.)
By convention you name those variables in upper-case, so in this case it would be $HANDLE instead. There should be no larger question marks regarding that issue.
I think that the small risk of clobbering the variable is out-matched by the warning feature of variables. It helps to keep code clean, and that is something maintainers also appreciate. I hate seeing unused subs/variables just because the author was too afraid/paranoid to remove them.
Cheers,
-Anomo
| [reply] [d/l] [select] |
|
I don't quite see the point in having a global function instead of a global variable
One thing it does buy you is the ability to drop a breakpoint on it in the debugger, and then play with caller to see where you were called from. That is not quite as simple to do with a variable.
In a farfetched scenario, you could also diddle the package's symbol table to wrap or replace the function itself. That's much harder to do with a variable.
print@_{sort keys %_},$/if%_=split//,'= & *a?b:e\f/h^h!j+n,o@o;r$s-t%t#u'
| [reply] |
Re: Avoiding global object handle
by Biker (Priest) on Mar 26, 2002 at 13:05 UTC
|
As per suggestion above, I have read up on Singleton. This proved to be very interesting reading indeed.
"The instance() method is used to create a new Class::Singleton instance, or return a reference to an existing instance. Using this method, it is only possible to have a single instance of the class in any system."
But it is not the solution to my situation. I want the object to exist in one instance per application within the system. Not one instance per system.
Everything went worng, just as foreseen.
| [reply] |
|
| [reply] |
•Re: Avoiding global object handle
by merlyn (Sage) on Mar 26, 2002 at 22:29 UTC
|
As the modules, written as OO modules, are written for reuse between applications there is no logical reason to instantiate more than one object of the class within the application using the module. At the moment of instantiation an object handle is retrieved and stored.
Then you don't really have instances any more. You have a singleton. And a package
is a fine singleton.
Don't make the same mistake as our favorite whipping boy, CGI.pm.
If you're always going to have at most one of a thing in an application, use class
methods, not instances methods, and lexical variables for member variables.
Fast. Easy. And subclassable, if done right.
-- Randal L. Schwartz, Perl hacker
| [reply] |
|
I see your point about classwide methods on a package
being a singleton, the class itself being like a single
instance with lexical member variables.
In practice, though, I have found that classwide methods
make it hard to replace functionality by subclassing,
because one needs to hardcode the package name everywhere
its methods are called. Then, when subclassing the singleton
package, one needs to replace all those calls to instead use
the name of the subclass. Do you know if there's a way
around this?
I think this is a shortcoming of using classwide methods.
Where I work we now instead favor having Factory classes
for producing singleton variables that we make available
as "framework globals" as described in my earlier posting.
This Factory class reads a configuration file in order to
determine what kind of subclass it should instantiate, which
works great for plug-and-play configuration of
different installs of our application. Example:
$FOO = FooFactory->get_foo();
As a convention we use all-caps for singleton variables.
| [reply] [d/l] |
|
So just use a string as the classname!
my $object = 'ThisClass';
...
$object->this;
$object->that;
-- Randal L. Schwartz, Perl hacker | [reply] [d/l] |
Re: Avoiding global object handle
by gregorovius (Friar) on Mar 26, 2002 at 18:15 UTC
|
I would go for the global variable, but hidden behind a
module which will not export it by default. You could call
this variable a "framework global", and is also a singleton.
package MyFramework;
use strict;
use Exporter;
use vars qw(@ISA @EXPORT @EXPORT_OK);
@ISA = qw(Exporter);
@EXPORT_OK = qw( $HANDLE );
1;
Thus, whenever you need to use it in a module, you'll
just have to put:
use MyFramework qw ( $HANDLE );
at the top of it and it'll be available for all methods in
that module. Where would you initialize this variable? As
early as possible, before any method requiring it is
executed, preferrably in a BEGIN block within the
MyFramework package itself.
This approach is equivalent to your "get_handle" method
because it ensures that the same instance will be used by
all. It also makes it easy to replace the class that
produces the handle, because it would only be invoked once in
the whole of your code, at the place where $HANDLE is
initialized.
| [reply] [d/l] [select] |
Re: Avoiding global object handle
by mamboking (Monk) on Mar 26, 2002 at 13:21 UTC
|
If the code that needs the handle is in a class, then you could pass the handle in on instantiation and save it as an attribute of the object. It would then be available to all of the methods of the class. | [reply] |
Re: Avoiding global object handle
by extremely (Priest) on Mar 26, 2002 at 22:21 UTC
|
You know, there is a lot of good ideas posted already, but in your situation, I'd probably just pass around a hash full of objects. You could probably tie the hash to a meta_new that tries to create your object if it doesn't already exist, I suppose.
Whenever I catch myself passing a bunch of the same arguments to myriad subs, I stuff em into a hash and just pass it around.
--
$you = new YOU;
honk() if $you->love(perl) | [reply] |