The 2004 Perl Advent Calendar
[about] | [archives] | [contact] | [home]

On the 3rd day of Advent my True Language brought to me..
Class::Accessor::Chained

One of the most dreary jobs when writing objects is creating accessor methods. These are the methods that people can use to simply get the current value and set new values for data stored in your object. They're often trivial to write, taking no more than six or so lines of code and consist of nothing more than storing and retrieving the values from the hash.

Of course, this being Perl, you can do without accessors if you want and directly access the elements inside the object hash. This is a bad idea however, as it totally busts your abstraction layer. If at a later date you want to put some code in to check that people are only setting values to a valid value, or you want to instead of storing the data directly in the hash in one field you want to compute it from several sources, you're stuffed. You really want the flexibility of accessors

So, we want the accessors, but we don't want to go though the hassle of writing, potentially debugging, and maintaining them. What we need is some way to automatically create the simple accessors programatically. This is where Class::Accessor and Class::Accessor::Chained comes in.

Let's consider a simple user object. The user has a firstname, a surname, and a date of birth (dob.) The simplest way to start coding it is like this:

  package User;
  # turn on perl's safety features
  use strict;
  use warnings;
  # a simple constructor
  sub new
  {
    my $class = shift;
    my $self  = bless {}, $class;
    return $self;
  }

Then we have to code a whole bunch of accessor methods for the firstname, surname and dob. We're going to be defining "get/set" methods - methods that return the value if you call them without any arguments, but if called with arguments instead set the value. Note that all three methods are exactly the same, they're just using different keys on the object hash.

  sub first
  {
     my $self = shift;
     $self->{first} = shift if @_;
     return $self->{first};
  }
  sub surname
  {
     my $self = shift;
     $self->{surname} = shift if @_;
     return $self->{surname};
  }
  sub dob
  {
     my $self = shift;
     $self->{dob} = shift if @_;
     return $self->{dob};
  }

To end this file we simply need to add a statement that returns a true value so Perl knows that it loaded the file okay when it uses the module.

  # return true
  1;

We can now get down to using this module in another class as we normally would, creating a new instance with the constructor...

  use User;
  my $user = User->new();

...and then use those accessor methods first to set values...

  # set their name
  $user->first("Jesus");
  $user->surname("Christ");
  # and their date of birth
  $user->dob( DateTime->new(
    year  => 1,
    month => 12,
    day   => 25,
  ));

...and then get the values back by calling the same methods with no arguments:

  print $user->first, " was born on ",
        $user->dob->ymd, "\n";

Improving Things with Class::Accessor

Though that approach worked we had to write a very large quantity of code to do what is a simple thing. Matters would be much worse if we had ten, twenty or fifty accessors. They'd be pages on pages of these accessor methods defined, each one potentially a place where we could make a typo (using the wrong key on the object hash for example) and introduce bugs.

What we need to do is have these simple methods automatically generated for us so we don't have to do any work. We could do this with a macro in our editor, but we've got Perl at our fingertips - why not just get it to do it for us?

By using Class::Accessor as a base class for our module we inherit the mk_accessors class method. When called this creates simple get/set methods in the class. The following code completely replaces the long drawn out code from above:

  package User;
  use base qw(Class::Accessor);
  # turn on perl's safety features
  use strict;
  use warnings;
  # define our accessors
  __PACKAGE__->mk_accessors(qw( first surname dob ));
  # return true;
  1;

You'll notice we've also done away with the constructor too. Class::Accessor defines a new method that simply returns a blessed hash reference of the passed class - and since we're inheriting from it we get that simple constructor for free.

Of course, just by using Class::Accessor we're not forced to define all our accessors with Class::Accessor. For example, if we wanted to rewrite our dob accessor to check that it was being passed a DateTime object, we could easily do that:

  package User;
  use base qw(Class::Accessor);
  # turn on perl's safety features
  use strict;
  use warnings;
  use Scalar::Util qw(blessed);
  # define our accessors
  __PACKAGE__->mk_accessors(qw( first surname ));
  sub dob
  {
    my $self = shift;
    # return the current value if no new value passed
    return $self->{dob} unless @_;
    # check we've been passed a DateTime
    my $dt = shift;
    die "Argument to dob method must be a DateTime"
      unless blessed($dt) && $dt->isa("DateTime");
    $self->{dob} = $dt;
  }
  # return true;
  1;

It just means for the simple accessors, we no longer have to write the code. Less effort all round, more time that the programmer can spend in the pub rather than increasing their RSI.

Chained Accessors

Let's consider for a moment the problem of object initialisation. We want to create a new object:

  my $mark = User->new();
  $mark->first("Mark");
  $mark->surname("Fowler");
  $mark->dob(DateTime->new( year => 1978, month => 3, day => 4 ));

Note how we repeat $mark each time. It's a lot of fiddly code to write, and can get really tiresome. The worst thing is when I copy and paste that code to create my second object (as I often do when I'm writing test cases), I have to search and replace all the $mark for $dave. What a pain. What we really want to do is write code to eliminate that.

One thing we can do is make our accessors chained. This means that when we set a new value for our accessor we return the object. An example method that does this looks something like this:

  sub first
  {
    my $self = shift;
    return $self->{first} unless @_;
    $self->{first} = shift;
    return $self;
  }

The advantage of this is that each time we set a value we get returned something that we can call another method on. So we can rewrite our code to look like this:

  # create object
  my $mark = User->new();
  # set up object
  $mark->first("Mark")
       ->surname("Fowler")
       ->dob(DateTime->new( year => 1978, month => 3, day => 4 ));

Or, since the last accessor in the chain also returns the object we can do the construction and initilisation in one step

  # create object
  my $mark = User->new()
                 ->first("Mark")
                 ->surname("Fowler")
                 ->dob(DateTime->new( year => 1978, month => 3, day => 4 ));

Creating chained methods automatically instead of normal get/set methods is trivial - instead of inheriting from Class::Accessor we inherit from Class::Accessor::Chained. That's all there is to it:

  package User;
  use base qw(Class::Accessor::Chained);
  # turn on perl's safety features
  use strict;
  use warnings;
  # define our accessors
  __PACKAGE__->mk_accessors(qw( first surname dob ));
  # return true;
  1;

  • Class::Accessor