13.12. Solving the Data Inheritance ProblemProblemYou want to inherit from an existing class, augmenting it with a few extra methods, but you don't know which data fields your parent class is using. How can you safely carve out your own namespace in the object hash without trampling on any ancestors? SolutionPrepend each of your fieldnames with your own class name and a distinctive separator, such as an underscore or two. DiscussionAn irksome problem lurks within the normal Perl OO strategy. The exact class representation must be known, violating the veil of abstraction. The subclass has to get unnaturally chummy with all its parent classes, recursively.
We'll pretend we're a big happy object-oriented family and that everyone always uses hashes for objects, thus dodging the problem of a class choosing an array representation but inheriting from one that instead uses a hash model. (The solution to that problem is aggregation and delegation, as shown in
perlbot
(1).) Even with this assumption, an inherited class can't safely use a key in the hash. Even if we agree to use only method calls to access attributes we don't ourselves set, how do we know that we aren't setting a key that a parent class is using? Imagine wanting to use a
One reasonable approach is to prefix your own data members with your package name. Thus if you were class Employee and wanted an sub Employee::age { my $self = shift; $self->{Employee_age} = shift if @_; return $self->{Employee_age}; } In the spirit of the Class::Struct module described in Recipe 13.5 , here's a more turnkey solution to the problem. Imagine one file with: package Person; use Class::Attributes; # see explanation below mkattr qw(name age peers parent); and another like this: package Employee; @ISA = qw(Person); use Class::Attributes; mkattr qw(salary age boss);
Notice that they both have an package Class::Attributes; use strict; use Carp; use Exporter (); use vars qw(@ISA @EXPORT); @ISA = qw(Exporter); @EXPORT = qw(mkattr); sub mkattr { my $hispack = caller(); for my $attr (@_) { my($field, $method); $method = "${hispack}::$attr"; ($field = $method) =~ s/:/_/g; no strict 'refs'; # here comes the kluglich bit *$method = sub { my $self = shift; confess "too many arguments" if @_ > 1; $self->{$field} = shift if @_; return $self->{$field}; }; } } 1;
This way
If you didn't want to write it that way, then from inside class Person, just use See Also
The documentation on the |
|