[ Team LiB ] Previous Section Next Section

11.4 Using AUTOLOAD for Accessors

Chapter 9 showed how to create color and set_color to get and set the color of an animal. If you had 20 attributes instead of one or two, the code would be painfully repetitive. However, using an AUTOLOAD method, you can construct the nearly identical accessors as needed, saving both compilation time and wear-and-tear on the developer's keyboard.

Use a code reference as a closure to do the job. First, set up an AUTOLOAD for the object and define a list of hash keys for which you want trivial accessors:

sub AUTOLOAD {
  my @elements = qw(color age weight height);

Next, you'll see if the method is a getter for one of these keys, and if so, install a getter and jump to it:

our $AUTOLOAD;
if ($AUTOLOAD =~ /::(\w+)$/ and grep $1 eq $_, @elements) {
  my $field = ucfirst $1;
  {
    no strict 'refs';
    *{$AUTOLOAD} = sub { $_[0]->{$field} };
  }
  goto &{$AUTOLOAD};
}

You need to use ucfirst because you named the method color to fetch the hash element called Color. The glob notation here installs a wanted subroutine as defined by the coderef closure, which fetches the corresponding key from the object hash. Consider this part to be magic that you just cut and paste into your program. Finally, the goto construct jumps into the newly defined subroutine.

Otherwise, perhaps it's a setter:

if ($AUTOLOAD =~ /::set_(\w+)$/ and grep $1 eq $_, @elements) {
  my $field = ucfirst $1;
  {
    no strict 'refs';
    *{$AUTOLOAD} = sub { $_[0]->{$field} = $_[1] };
  }
  goto &{$AUTOLOAD};
}

If it is neither, death awaits:

  die "$_[0] does not understand $method\n";
}

Again, you pay the price for the AUTOLOAD only on the first hit of a particular getter or setter. After that, a subroutine is now already defined, and you can just invoke it directly.

    [ Team LiB ] Previous Section Next Section