I’m toying around with some code where bundling coderefs with variables makes sense, but using full blown OOD doesn’t. Basically, I’m writing functional code but resorting to a select few OO-style structures to encapsulate stuff where statefulness really makes sense. Outside that it’s mostly state-free functions.
Using Moose for this would be like disciplining occasionally-late coworkers with an automatic rifle: complete overkill in more ways than one. So spare me the ‘ALL PERL OOP MUST USE MOOSE NOW!!!’ mantra.
So I ran to the good ol’ blessed-hashref class to do my bidding, but found that writing accessor code is a pain, and didn’t want to pull in non-standard modules for what is essentially a very trivial task.
After shuffling through pages on http://perldoc.perl.org, I stumbled across Class::Struct and Object::Accessor, which are core modules that ease the construction of OO attributes. Perfect, I though. But…
Object::Accessor seemed like the perfect ticket, but I was simply using its mk_accessor sub in the constructor, which didn’t seem anywhere near as elegant as using compile-time Class::Struct, which also wrote my default constructor for me. All in all, I preferred Class::Struct to Object::Accessor because allowed slightly less boilerplate and had a more declarative syntax.
A comparison using trivial examples, in case you’re wondering:
Using Class::Struct:
#!/usr/bin/perl
use 5.014;
use autodie;
use strict;
use warnings;
package Account {
use Class::Struct
first_name => '$',
last_name => '$',
age_in_years => '$',
activated => '$',
;
sub formatted {
my ($self_ref) = @_;
return sprintf
"First name: %s\n" .
"Last name: %s\n" .
"Age in Years: %d\n" .
"Activated: %s",
$self_ref->first_name,
$self_ref->last_name,
$self_ref->age_in_years,
$self_ref->activated || 'No'
;
}
}
my $account = Account->new;
$account->first_name('Tom');
$account->last_name('Smith');
$account->age_in_years(16);
$account->activated('Yes');
say $account->formatted
Using Object::Accessor:
use 5.014;
use autodie;
use strict;
use warnings;
package Account {
use base 'Object::Accessor';
sub new {
my ($type) = @_;
my $self = bless { }, $type;
$self->mk_accessors(qw(first_name last_name age_in_years activated));
return $self;
}
sub formatted {
my ($self_ref) = @_;
return sprintf
"First name: %s\n" .
"Last name: %s\n" .
"Age in Years: %d\n" .
"Activated: %s",
$self_ref->first_name,
$self_ref->last_name,
$self_ref->age_in_years,
$self_ref->activated || 'No'
;
}
}
my $account = Account->new;
$account->first_name('Tom');
$account->last_name('Smith');
$account->age_in_years(16);
$account->activated('Yes');
say $account->formatted;
Really, both are perfectly acceptable, but the former is quite a bit cleaner IMO. What I want to know, is, can I use Class::Struct without specifying the type constraints?
I don’t need them in the context I’m using them, yet I don’t seem to be able to just add accessors without specifying type constraints alongside them. No doubt I loose performance due to unnecessary type checking too.
If this isn’t possible, I’ll just stick to Object::Accessor. But are non-type-constrained accessors possible with Class::Struct?
I prefer Class::Struct for the task. I am usually using only scalars, they have no validation and you can store anything to them (like arrayref or hashref).
Another advantage is that syntax you’ve choosen is making your objects based on arrays, which are smaller and have faster access. Nothing dramatic, but if you have many objects, it can help.