Re: Class::Accessor and Damian's PBP
by Roy Johnson (Monsignor) on Feb 22, 2006 at 19:40 UTC
|
If you have so many accessible properties that you need to generate accessors for them, you may need to re-think your design. Accessors are a code smell.
But assuming you have good reason to have so many accessors, I'm sorry to say I don't have a satisfactory answer to your question. I would go with Class::Accessor::Lvalue because I think that something that behaves like a variable (being settable and gettable) should have an interface like a variable. It is also very clear about when you're setting and when you're getting. If you then use Class::Accessor for the read-only accessors, everything is taken care of.
Caution: Contents may have been coded under pressure.
| [reply] |
|
If you have so many accessible properties that you need to generate accessors for them, you may need to re-think your design. Accessors are a code smell.
I'm not sure that I entirely agree with that assertion.
Most OO classes will have a number of properties, but unfortunately Perl's object model does not provide a standard way to define those properties. The simple and common approach is to store properties as keys in a hash but this leads to hard-to-find bugs resulting from typos in hash keys. Generating accessors is an effective way to eliminate that class of error.
I do agree that lots of accessors in the public API of a class is often a problem with the design.
The examples on the page you linked to seem to be written in a language that, unlike Perl, supports both properties and private methods.
| [reply] |
|
| [reply] [d/l] |
|
| [reply] |
|
Whenever your setter needs to be normalized to a different value lvalue accessors don't cut it.
| [reply] |
|
use Contextual::Return;
# Name method offers unconstrained access...
sub name : lvalue {
my ($self) = @_;
$name_of{ident $self};
}
# Rank method constrains access: can assign only valid ranks
# (value being assigned is passed as $_ to LVALUE block)...
sub rank : lvalue {
my ($self) = @_;
LVALUE {
croak "Invalid rank ($_) assigned" if !$is_valid_rank{$_};
$rank_of{ident $self} = $_;
}
RVALUE {
$rank_of{ident $self};
}
}
# Serial method offers read-only access...
sub serial {
my ($self) = @_;
return $snum_of{ident $self};
}
With this functionality now available, I'm seriously reconsidering my long-held objections to lvalue accessors.
Damian | [reply] [d/l] [select] |
|
|
| [reply] |
|
|
|
|
If you have so many accessible properties that you need to generate accessors for them, you may need to re-think your design.
I'm working on a configuration module, so accessors and data are the primary purpose of the module. You can see some background in Configuration Best Practices for Web Apps.
Even if you don't have many, though, I would argue the good kind of Laziness should drive you to use tools to avoid cut and paste. Even with just a few accessors, the code often ends up being largely cut and pasted, so using a module to automate the process is still a nice idea.
| [reply] |
|
After playing around in several directions, I realized that it's trivially easy to generate default accessors, which may be why there are so many modules for it (although it makes me wonder why there are any).
for my $property (qw(several members defined public)) {
no strict 'refs';
*{"get_$property"} = sub { (shift)->{$property} };
*{"set_$property"} = sub { (shift)->{$property} = $_[0] };
}
Caution: Contents may have been coded under pressure.
| [reply] [d/l] |
|
|
| [reply] |
|
| [reply] |
Re: Class::Accessor and Damian's PBP
by nothingmuch (Priest) on Feb 22, 2006 at 21:06 UTC
|
| [reply] |
|
Thanks for the pointer.
It appears CDBI is using Class::Accessor in the currently support method of overriding get and set. After looking at Class::Accessor, I think what I want is a way to provide my own alias that is different from the actual field name, which isn't supported right now.
| [reply] |
|
Class::DBI has a way to make this interface for your persistent objects:
$row_obj->set_field("foo");
$row_obj->get_field();
# instead of
$row_obj->field("foo");
$row->obj_field();
That's what you meant, right?
Look for mutator_name and accessor_name - this is under http://search.cpan.org/~tmtm/Class-DBI-v3.0.14/lib/Class/DBI.pm#Changing_Your_Column_Accessor_Method_Names.
Specifically, look at
sub mutator_name_for {
my ($class, $column) = @_;
return "set_" . $column->accessor;
}
And copy over the implementation of the code that uses mutator_name to create write only accessors and readonly accessors.
| [reply] [d/l] [select] |