Re: "Fields" for "Objects"
by Your Mother (Archbishop) on Jun 10, 2009 at 18:24 UTC
|
You should look at Moose. It's better OOP. If you're in a hurry you can also check out Class::Accessor which is passable but nowhere near as rich as Moose.
| [reply] |
Re: "Fields" for "Objects"
by webfiend (Vicar) on Jun 10, 2009 at 19:10 UTC
|
Listen to Your Mother.
I thought you might be curious about the specifics of how the advice to use Moose would help in this case, though. The functionality of your code above would be replaced by:
package Whatever;
use Moose;
has 'main_message' => ( is => 'rw' );
Poof! Instant access to $whatever->main_message and $whatever->main_message($value). | [reply] [d/l] [select] |
Re: "Fields" for "Objects"
by stvn (Monsignor) on Jun 10, 2009 at 19:39 UTC
|
Poof! Instant access to $whatever->main_message and $whatever->main_message($value).
And if you dont like that style accessor there is MooseX::FollowPBP for Perl Best Practices style accessors (like you have in your example), MooseX::SemiAffordanceAccessor (for foo/set_foo style) and MooseX::Accessors::ReadWritePrivate which pretty much handles everything else (so the docs seem to indicate, I haven't used it myself).
| [reply] |
Re: "Fields" for "Objects"
by vek (Prior) on Jun 10, 2009 at 22:52 UTC
|
I usually write getter/setter combos:
sub accountnumber {
my ($self, $num) = @_;
if (defined($num)) {
$self->{account_num} = $num;
return $self;
} else {
return $self->{account_num};
}
}
But even that's a pain in the arse too. So, Class::Accessor for the win.
use base qw(Class::Accessor);
__PACKAGE__->mk_accessors(qw(accountnumber));
| [reply] [d/l] [select] |
|
It's interesting, this is how I write them too. But I noticed lodin's node, which contains an important but subtle difference; by doing this:
sub field {
my $self = shift;
$self->{field} = $_[0] if @_;
...
}
instead of this:
sub field {
my ($self,$val) = @_;
$self->{field} = $val if defined $val;
...
}
you don't run into issues where $self->field(undef) would silently return the old value without changing it, which isn't what you might expect. But because I've seen the second form elsewhere and it's technically better practice to assign @_ to variables I just stuck with it, but clearly the first way is better. | [reply] [d/l] [select] |
Re: "Fields" for "Objects"
by lakshmananindia (Chaplain) on Jun 11, 2009 at 03:33 UTC
|
Class::Struct will also provide this accessors functionality
--Lakshmanan G.
The great pleasure in my life is doing what people say you cannot do.
| [reply] |
Re: "Fields" for "Objects"
by Transient (Hermit) on Jun 10, 2009 at 18:15 UTC
|
At the bottom of perltoot there is a nice way to use AUTOLOAD to generate the getters and setters for your objects.
(The following code is directly from the link):
package Person;
use Carp;
our $AUTOLOAD; # it's a package global
my %fields = (
name => undef,
age => undef,
peers => undef,
);
sub new {
my $class = shift;
my $self = {
_permitted => \%fields,
%fields,
};
bless $self, $class;
return $self;
}
sub AUTOLOAD {
my $self = shift;
my $type = ref($self)
or croak "$self is not an object";
my $name = $AUTOLOAD;
$name =~ s/.*://; # strip fully-qualified portion
unless (exists $self->{_permitted}->{$name} ) {
croak "Can't access `$name' field in class $type";
}
if (@_) {
return $self->{$name} = shift;
} else {
return $self->{$name};
}
}
Which is basically going to "auto build" anything that's set in your permitted hash. I've used this before and it's saved a great deal of time (and tediousness).
I know this is a different approach than what you asked for (i.e. a specific module), but I hope it helps. It also combines your calls for get/set into a single method. | [reply] [d/l] [select] |
|
Honestly, use of AUTOLOAD for this kind of stuff is just really a bad idea. You will outgrow this solution all too quickly, not to mention the fact that your code is broken once you introduce inheritance since you have no mechanism to inherit the fields. Sure that problem is solve-able but not without a bunch of tedious plumbing as well. It should also be noted that AUTOLOAD introduces something like a 400% performance penalty over normal method calls (which only gets bigger the more complex your AUTOLOAD gets).
| [reply] |
|
sub AUTOLOAD {
my $self = shift;
my $type = ref($self)
or croak "$self is not an object";
my $name = $AUTOLOAD;
$name =~ s/.*://; # strip fully-qualified portion
unless (exists $self->{_permitted}->{$name} ) {
croak "Can't access `$name' field in class $type";
}
my $sub = sub {
my ($self,$value) = @_;
$self->{$name} = $value if defined $value;
$self->{$name};
};
{
no strict 'refs';
*{$name} = $sub;
}
goto $sub;
}
Each subsequent call to the method accesses the sub normally, i.e. through typeglob lookup.
not to mention the fact that your code is broken once you introduce inheritance since you have no mechanism to inherit the fields. Sure that problem is solve-able but not without a bunch of tedious plumbing as well.
That plumbing amounts to writing a proper import() subroutine which calls AUTOLOAD for the inherited fields and exports them to the inheriting class. But then, you're right, why reinvent the wheel (there are reasons, though) if there are plenty modules out there which handle that.
There's Moose, classes, Class::Accessor, ... | [reply] [d/l] [select] |
|
|
|
The only good reason to use AUTOLOAD is when you already know you need to use it. It has a magic-from-afar type behavior that can be weird.
| [reply] |
|
my @fields = qw/
name
age
peers
/;
for my $name (@fields) {
my $code = sub {
my $self = shift;
$self->{$name} = $_[0] if @_;
return $self->{$name};
};
no strict 'refs';
*$name = $code;
}
Now your class acts exactly like it would have acted if you manually wrote the methods, and you pay no run-time penalties.
Note how simple the logic of the code is compared to AUTOLOAD. And the AUTOLOAD implementation will get more complicated still. Consider what happens when you
- do $obj->can('age'),
- refactor and create a superclass that also uses AUTOLOAD, or
- use multiple inheritance where another class also uses AUTOLOAD.
- (Or forget to do return if $name eq 'DESTROY';.)
A proper implementation of AUTOLOAD requires an awful lot of thinking, and still it does not buy you anything, rather the opposite, if you already know the method names.
lodin | [reply] [d/l] [select] |
Re: "Fields" for "Objects"
by mamboking (Monk) on Jun 11, 2009 at 13:44 UTC
|
You may want to reconsider why you're using so many accessors. More often than not you're violating encapsulation. Check out this article for more information: Accessors Are Evil. | [reply] |
Re: "Fields" for "Objects"
by cbrandtbuffalo (Deacon) on Jun 11, 2009 at 14:32 UTC
|
Another vote for Class::Accessor. The author also added a 'follow_best_practice' mode a while ago so you can create getters and setters following the recommendation in Damian's book. | [reply] |
Re: "Fields" for "Objects"
by targetsmart (Curate) on Jun 11, 2009 at 03:53 UTC
|
you can get this functionality with the help of AUTOLOAD, see Re: autoload usage
This code could be seen in the examples of the book 'Advanced Perl Programming' by sriram srinivasan.
Vivek
-- In accordance with the prarabdha of each, the One whose function it is to ordain makes each to act. What will not happen will never happen, whatever effort one may put forth. And what will happen will not fail to happen, however much one may seek to prevent it. This is certain. The part of wisdom therefore is to stay quiet.
| [reply] |