Using the Perl 'class' feature
I'm not really deep into class design, Metaobject protocols and object-oriented programming, but one feature I've come to like with Perl is using roles to mix-in methods that are convenient to have but tedious to reimplement ad-hoc. Roles can be implemented via multiple inheritance, but they (try to) avoid problems by being designed more around adding self-contained features and not overriding methods in the descendant class.
For example MooX::Role::DBIConnection
gives a class a lazy ->dbh accessor and a nice way to supply database credentials in
the constructor. I'm not claiming that this is the best way to approach this, but it makes
managing the credentials and constructing the database handle very convenient.
{ package My::Example;
use Moo 2;
with 'MooX::Role::DBIConnection';
};
# Lazily connect using the parameters
my $writer = My::Example->new(
dbh => {
dsn => '...',
user => '...',
password => '...',
options => '...',
},
);
my $count = $writer->dbh->selectall_arrayref("select count(*) from mytable")->[0]->[0];
The other thing that roles, or at least Moo::Role
brings to the table is checking that the target class the role is applied to provides
some methods, using the requires keyword. This checks the contract
at compile time instead of needing to exercise the code in the test suite.
I spent some hours revisiting Protocol::Samsung::MDC
to talk to my Samsung EM32DX,
and I reimplemented it using the "new" class
feature, using a Perl-built-in object model. The class feature is still experimental
and the finer points are still being worked on, but for my use-case it worked quite well.
One thing I was missing was a convenient way to emit events from an object. Emitting events makes for a (IMO) nicer code structure than hooking up callbacks, as I don't have to set up all callbacks in the constructor, and have a centralized dispatch mechanism for all events within an object.
package main;
my $foo = My::Thing->new();
$foo->on( myevent => sub( $ev ) {
say "I receivend an event";
});
The basic code is well-tested, originally in Mojo::EventEmitter, and also available outside of Mojolicious as Role::EventEmitter and my almost identical MooX::Role::EventEmitter.
The class feature has no provision yet on how to interact with bless-based classes, so reusing these
modules did not work. But copying the code into a class-based module did work, with the
one thing missing - no implicit checking of methods and fields in the target class:
use experimental 'class';
class My::Thing :isa(PerlX::Role::EventEmitter);
use 5.020;
use experimental 'signatures';
sub event_received( $self, $ev ) {
$self->emit( myevent => $ev );
}
...
Maybe one could implement the check for the required members and functions in the
ADJUST phase of the role, but as some day the class feature might gain native
support for roles (or acquire a sibling role keyword), I'm happy to let my test
suite suss out missing methods.