Instead of writing a new function every time we want a new data field,
we'll use the autoload mechanism to generate (actually, mimic) methods on
the fly. To verify that we're accessing a valid member, we will check
against an _permitted
(pronounced ``under-permitted'') field, which is a reference to a file-scoped lexical (like a
C file static) hash of permitted fields in this record called %fields. Why the underscore? For the same reason as the
_CENSUS field we once used: as a marker that means ``for internal use only''.
Here's what the module initialization code and class constructor will look like when taking this approach:
package Person; use Carp; use vars qw($AUTOLOAD); # it's a package global
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; }
If we wanted our record to have default values, we could fill those in
where current we have undef in the %fields
hash.
Notice how we saved a reference to our class data on the object itself?
Remember that it's important to access class data through the object itself
instead of having any method reference %fields
directly, or
else you won't have a decent inheritance.
The real magic, though, is going to reside in our proxy method, which will handle all calls to undefined methods for objects of class Person (or subclasses of Person). It has to be called AUTOLOAD. Again, it's all caps because it's called for us implicitly by Perl itself, not by a user directly.
sub AUTOLOAD { my $self = shift; my $type = ref($self) or croak "$self is not an object";
my $name = $AUTOLOAD; $name =~ s/.*://; # strip fully-qualified portion
unless (exists $self->{_permitted}->{$name} ) { croak "Can't access `$name' field in class $type"; }
if (@_) { return $self->{$name} = shift; } else { return $self->{$name}; } }
Pretty nifty, eh? All we have to do to add new data fields is modify %fields. No new functions need be written.
I could have avoided the _permitted
field entirely, but
I wanted to demonstrate how to store a reference to
class data on the object so you wouldn't have to access that class data
directly from an object method.