Документация
HTML CSS PHP PERL другое

Section 14.5.  Creating Getters and Setters More Easily

 
Previous
Table of Contents
Next

14.5. Creating Getters and Setters More Easily

If all that coding for creating accessors using AUTOLOAD looks messy, rest assured that we really don't need to tackle it, because there's a CPAN module that does it a bit more directly: Class::MethodMaker.[*]

[*] Sometimes Class::MethodMaker can be a bit much. We can also check out the lighter Class::Accessor.

For example, a simplified version of the Animal class might be defined as follows:

package Animal;
use Class::MethodMaker
  new_with_init => 'new',
  get_set => [-eiffel => [qw(color height name age)]],
  abstract => [qw(sound)],
;
sub init {
  my $self = shift;
  $self->set_color($self->default_color);
}
sub named {
  my $self = shift->new;
  $self->set_name(shift);
  $self;
}
sub speak {
  my $self = shift;
  print $self->name, ' goes ', $self->sound, "\n";
}
sub eat {
  my $self = shift;
  my $food = shift;
  print $self->name, " eats $food\n";
}
sub default_color {
  'brown';
}

The getters and setters for the four instance attributes (name, height, color, and age) are defined automatically, using the method color to get the color and set_color to set the color. (The eiffel flag says "do it the way the Eiffel language does it," which is the way it should be done here.) The messy blessing step is now hidden behind a simple new method. We define the initial color as the default color, as before, because the generated new method calls the init method.

However, we can still call Horse->named('Mr. Ed') because it immediately calls the new routine as well.

Class::MethodMaker generated the sound method as an abstract method. Abstract methods are placeholders, meant to be defined in a subclass. If a subclass fails to define the method, the method Class::MethodMaker generates for Animal's sound dies.

We lose the ability to call the getters (such as name) on the class itself, rather than an instance. In turn, this breaks our prior usage of calling speak and eat on generic animals, since they call the accessors. One way around this is to define a more general version of name to handle either a class or instance and then change the other routines to call it:

sub generic_name {
  my $either = shift;
  ref $either ? $either->name : "an unnamed $either";
}
sub speak {
  my $either = shift;
  print $either->generic_name, ' goes ', $either->sound, "\n";
}
sub eat {
  my $either = shift;
  my $food = shift;
  print $either->generic_name, " eats $food\n";
}

There. Now it's looking nearly drop-in compatible with the previous definition, except for those friend classes that referenced the attribute names directly in the hash as the initial-cap-keyed versions (such as Color) rather than through the accessors ($self->color).

That brings up the maintenance issue again. The more we can decouple our implementation (hash versus array, names of hash keys, or types of elements) from the interface (method names, parameter lists, or types of return values), the more flexible and maintainable our system becomes.

The flexibility isn't free, however. Since the performance cost of a method call is higher than the cost of a hash lookup, in some circumstances it may make sense to have a friend class peek inside.


Previous
Table of Contents
Next
© 2000- NIV