Home > Software design >  Default Perl accessor for objects
Default Perl accessor for objects

Time:09-07

If I have a Perl class eg

package Foo;

sub new {
    my ($class,$hashref) = @_;
    my $self = bless $hashref, $class;
}

and initialised with

my $foo = Foo->new( { bar => 2, othervar => 8 } );

I can do

print $foo->{ bar };

which feels clunky, and

print $foo->bar

feels more preferable. However, if there are a lot of keys, I'd prefer not to have to write an accessor for every key (or is that best practice) ?

So, I can include

our $AUTOLOAD;
sub AUTOLOAD {
    my $self = shift;

    my $called =  $AUTOLOAD =~ s/.*:://r;

    die "No such attribute: $called"
        unless exists $self->{$called};

    return $self->{$called};
}

sub DESTROY { } # see below

In perldoc perlobj it says # XXX - this is a terrible way to implement accessors

Are there any good ways to implement accessors like this, without using other packages, eg Moose, Class::Accessor ? I'm just after something light as its just one class that has a lot of keys.

CodePudding user response:

Are there any good ways to implement accessors like this, without using other packages ...

If you insist, then write those subs directly to the package symbol table

package AutoAccessors;

use warnings;
use strict;
use feature 'say';

my @attr_names;

BEGIN {
    @attr_names = qw(name mode etc);
    no strict 'refs';
    foreach my $accessor (@attr_names) {
        *{$accessor} = sub { do {
            if    (@_ == 1) { $_[0]->{$accessor} }
            elsif (@_ == 2) { $_[0]->{$accessor} = $_[1] }
            #elsif ...
        } };
    }
};


sub new {
    my ($class, $args) = @_;
    my $self;
    foreach my $attribute (@attr_names) {
        # Check, initialize, set from $args, etc 
        $self->{$attribute} = $args->{$attribute} if $args->{$attribute};
    }   
    return bless $self, $class;
}   

1;

Then

use warnings;
use strict;
use feature 'say';

use AutoAccessors;

my $obj = AutoAccessors->new({ mode => '007' }); 

$obj->name('Bond');
say "name's ", $obj->name;
say "mode:  ", $obj->mode;

This is done in a number of CPAN packages (and it's usually more elaborate).

Having said that, I see no good reason to avoid good libraries, far more carefully done and complete. For instance, Moo as a full system comes in at around 5 kloc (if I recall correctly) and has barely a handful of dependencies, while Class::Accessor is just over 200 loc with one dependency that I can see.

  •  Tags:  
  • perl
  • Related