For various reasons, I'm trying to brush up on my Perl skills, specifically focusing on Perl's OO framework. This involves reading the relevant man pages on the topic.
One thing that I've always been wary of playing with is the AUTOLOAD feature. I've known of its existence, but I haven't really had a chance to play with it. In a script I wrote last week I compared the AUTOLOAD method of defining methods to the more traditional way of defining a accessor/mutator method for each instance variable. With correct preparation, the AUTOLOAD way is a lot easier than the other way (even if you use a common function to do the work).
One of the disadvantages is that you lose some degree of control, which you have to build back into your object. The first thing to worry about is what instance variables are allowed. Controlling this is achievable through the method supplied in the perltoot man page:
my %fields = (
name => undef,
age => undef,
peers => undef,
);
sub new {
my $that = shift;
my $class = ref($that) || $that;
my $self = {
_permitted => \%fields,
%fields,
};
bless($self, $class);
return($self);
}
This works really nicely because your AUTOLOAD method can inspect how it was called (ie: the name) and determine what it member its working on. Then it can consult the _permitted instance member to determine if that member exists, and can raise an error if it doesn't. This method also automatically populates and initialises the instance variables for you. The downside of this is that if you inherit from this you have to work a little harder to get it to work right. Also, you still can't enforce typing (which can be handy if you don't trust code).
A better combination that I'm thinking about is to define an object framework which handles initialisation, typing, and inheritance. Essentially its the same as above, but %fields is modified to contain initialisation and type information too. You then call a method to insert the modified %fields has for you, and populates the type information in the _permitted field. Easy. It could even be extended to think about permissions (eg: public, private, protected), and even extra code snippets to run if required (but you might as well explicitly define a function in that case). Coupling this with AUTOLOAD should make things nice and easy.
Another problem I considerred is that the AUTOLOAD'ed methods may not cater for everything when inherited, or you may want to autoload your methods and a super class is is autoloading some of theirs (or maybe not - who knows?). Basically, if you define an AUTOLOAD method, you may want to defer to a superclasses AUTOLOAD method for things that you can't handle. So I wanted to figure out how to do that:
sub AUTOLOAD {
my $self = shift;
my @args = @_;
my $sub = $AUTOLOAD;
$sub =~ s/.*://; # strip fully-qualified portion
# my stuff goes here
# defer to super's autoloaded methods
$sub = "SUPER::$sub";
return($self->$sub(@args));
}
It has problems still. For example, if DESTROY is called if there's no SUPER::AUTOLOAD and no defined DESTORY method, you'll get an error. I think the correct way of handling that would be to create your own DESTROY method, and then pay particular attention to the class you inherit from and call its DESTROY method if available and appropriate. (I tried calling SUPER::DESTROY from DESTROY when it didn't exist, and it didn't raise an error, even when using strict pragma and warnings, so perhaps the generall guideline would be "if you defined AUTOLOAD, define DESTROY and have it call SUPER::DESTROY").
I'm interested in how other people tackle the problem, because I'm just making this shit up as I go along. I don't know what the "best practices" are in this area - perhaps its just safer to leave the whole AUTOLOAD thing alone for the time being...