Is it proper to get and especially set Perl module's global variables directly?
I was wondering what the best practice in Perl is regarding getting开发者_Python百科 - or, more importantly, setting - a global variable of some module by directly accessing $Module::varName
in case the module didn't provide getter/setter method for it.
The reason it smells bad to me is the fact that it sort of circumvents encapsulation. Just because I can do it in Perl, I'm not entirely certain I should (assuming there actually is an alternative such as adding a getter/setter to the module).
It isn't violating encapsulation if the variable is part of the public API. (If it isn't that's another matter.)
I think direct access is preferable as it allows you to take advantage of dynamic scoping:
local $Module::varName = 42;
This makes conflicts with other code using Module
less likely.
Global module variables were in vogue in the past, but considered "bad form" as an interface in Modern Perl. It's important to recognize that Perl is 22-23 years old now, and styles and practices have changed. :) Do note though that there are times when it's still appropriate, because there's some very nice features that come along with package variables. It's as usual a matter of experience and practice to see what a good solution might be.
To understand the best use for package variables, you really need to understand how local
works. Check out local
's perldoc help. Local lets you take a package variable like (as an example) $My::Variable
in the package My
, and create a dynamically scoped version of it. Normally if you change $My::Variable
in place, it will affect your entire program, and will persist. For small programs, that might not be a big deal. For large ones, this can have disastrous side effects. local
lets you make a temporary change to that variable, that's limited to your current scope.
Here's how it works:
use 5.012;
use warnings;
package My;
our $Variable = 5;
package main;
say $My::Variable; # prints 5
$My::Variable = 7;
say $My::Variable; # prints 7
{ # create a new lexical scope
local $My::Variable = 10; # create a new dynamic scope for $My::Variable
# that will persist to the end of the lexical scope
say $My::Variable; # prints 10
}
say $My::Variable; # end of the lexical scope for the localized
# $My::Variable, so prints 7 again
Effectively, it lets you use package variables in a safe way. Unfortunately, not everyone knows about local, so they often clobber the global variable. Documenting good use (eg, local
) always helps.
A getter/setter with proper object encapsulation prevents a lot of this, but not always. To make it work the way a local variable does, you'd have to do a lot of extra work. The nicest thing about being able to localize a package variable is that you can do temporary changes very easily, say, for a debug variable. Normally, you have to do a pattern like:
{
my $current_variable My::get_variable();
$My::set_variable($new_value);
# Do code work
$My::set_variable($current_variable);
}
With local, this becomes:
{
local $My::Variable = $new_value;
# do code work
}
(Incidentally, I wish you could do this to lexical variables too, for the same reason...but you can't.) So, for some things, package variables can make sense. It depends on how you want to use it. Things like
- Debugging Variables
- Global configuration that doesn't/shouldn't be changed often
However, if it's something that does need to be changed on a regular basis, like
- Regularly used variables (see the horrible interface for
File::Find
) - Temporary configuration
- "Object" variables
Basically, anything that needs to be modified more than once or in rare situations, or should otherwise be encapsulated into a generated object, then I'd avoid the package variable.
If the module doesn't provide an accessor, create one, use it, and send in the patch.
精彩评论