Your object needs accessor methods to set or get its data fields, and you're tired of writing them all out one at a time.
Carefully use Perl's AUTOLOAD mechanism as a proxy method generator so you don't have to create them all yourself each time you want to add a new data field.
Perl's AUTOLOAD mechanism intercepts all possible undefined method calls. So as not to permit arbitrary data names, we'll store the list of permitted fields in a hash. The AUTOLOAD method will check to verify that the accessed field is in that hash.
package Person; use strict; use Carp; use vars qw($AUTOLOAD %ok_field); # Authorize four attribute fields for my $attr ( qw(name age peers parent) ) { $ok_field{$attr}++; } sub AUTOLOAD { my $self = shift; my $attr = $AUTOLOAD; $attr =~ s/.*:://; return unless $attr =~ /[^A-Z]/; # skip DESTROY and all-cap methods croak "invalid attribute method: ->
$attr()"
unless $ok_field{$attr}; $self->{uc $attr} = shift if @_; return $self->{uc $attr}; } sub new { my $proto = shift; my $class = ref($proto) || $proto; my $parent = ref($proto) && $proto; my $self = {}; bless($self, $class); $self->parent($parent); return $self; } 1;
This class supports a constructor named
new
, and four attribute methods:
name
,
age
,
peers
, and
parent
. Use the module this way:
use Person; my ($dad, $kid); $dad = Person->new; $dad->name("Jason"); $dad->age(23); $kid = $dad->new; $kid->name("Rachel"); $kid->age(2); printf "Kid's parent is %s\n", $kid->parent->name;
Kid's parent is Jason
This is tricky when producing inheritance trees. Suppose you'd like an Employee class that had every data attribute of the Person class, plus two new ones, like
salary
and
boss
. Class Employee can't rely upon an inherited
Person::AUTOLOAD
to determine what Employee's attribute methods are. So each class would need its own
AUTOLOAD
function. This would check just that class's known attribute fields, but instead of croaking when incorrectly triggered, it would call its overridden superclass version.
Here's a version that takes this into consideration:
sub AUTOLOAD { my $self = shift; my $attr = $AUTOLOAD; $attr =~ s/.*:://; return if $attr eq 'DESTROY'; if ($ok_field{$attr}) { $self->{uc $attr} = shift if @_; return $self->{uc $attr}; } else { my $superior = "SUPER::$attr"; $self->$superior(@_); } }
If the attribute isn't in our OK list, we'll pass it up to our superior, hoping that it can cope with it. But you can't inherit this
AUTOLOAD
; each class has to have its own, because it is unwisely accessing class data directly, not through the object. Even worse, if a class A inherits from two classes B and C, both of which define their own
AUTOLOAD
, an undefined method call on A will hit the
AUTOLOAD
in only one of the two parent classes.
We could try to cope with these limitations, but
AUTOLOAD
eventually begins to feel like a kludge piled on a hack piled on a workaround. There are better approaches for the more complex situations.
The examples using AUTOLOAD in perltoot (1); Chapter 5 of Programming Perl ; Recipe 10.15 ; Recipe 13.12
Copyright © 2001 O'Reilly & Associates. All rights reserved.