Class::Lego::Myself - Classes with a default object
package My:Class; use Class::Lego::Myself; __PACKAGE__->give_my_self(); sub doit { my $self = &find_my_self; ... } # somewhere else $my = My::Class->new(); $my->doit(); # normal method call # My::Class->doit(); # doit() invoked over the default object
This module is a minor helper to write OO modules with methods that work on some default object when called as class methods.
Suppose you're writing a nice OO module My::Class with a handy method doit. If customization is needed, the usage follows the example:
My::Class
doit
my $obj = My::Class->new( @new_args ); $obj->doit();
However, most of the time, your users will not be interested in the neat OO design underneath and some default object will be good enough. So a nice interface would be to use doit as a class method too.
My::Class->doit(); # no need to create an instance
You may do that with this module. In two slightly different ways: (1) importing a helper function give_my_self and (2) inheriting from it.
give_my_self
# importing &give_my_self package My::Class; use Class::Lego::Myself; #use parent qw( Class::Lego::Myself ); # if you prefer IS-A relationship __PACKAGE__->give_my_self();
After you called give_my_self, your package is now endowed with a new sub find_my_self which should be used in every method of the public interface intented to work both as an instance method and as a class method. So the first lines of doit should look like this:
find_my_self
sub doit { my $self = &find_my_self; ... }
The task of find_my_self is to bypass a reference if that's the invocant or to replace it with the default object as if
My::Class->doit();
were equivalent to
My::Class->get_default_object()->doit();
By default, the default object is constructed by $class->new where $class is the package over which give_my_self was called.
$class->new
$class
The default object is always the same (that is, a singleton). The default object is not saved into a package variable or something like that. (Actually it resides in closures that make it accessible only to the code which needs it.) So it can't pollute your namespace and hopefully will keep us out of trouble with hard-to-track bugs.
$package->give_my_self; $package->give_my_self({ default => $coderef, });
Install find_my_self into the receiver. The default object is computed once from the $coderef given in the key 'default' of the hashref argument. The default $coderef is:
$coderef
'default'
sub { $package->new() };
An explicit coderef may pass additional constructor arguments and do other things you want:
sub { $package->new(@customized_args); }
my $self = &find_my_self; (my $self, @_) = &find_my_self; my ($self, @args) = &find_my_self;
This function examines the first argument and leaves it alone if it is a ref. Otherwise, replaces it with the corresponding default object (established when give_my_self was called). The first argument is shifted from the argument list.
In scalar context, it returns the computed invocant. In list context, it returns the computed invocant and the the rest of the argument list.
The usage forms above are just handy ways to make current @_ visible to find_my_self (as explained in perlsub). Otherwise, you could pass @_ explicitly but may need to update it.
@_
my $self = find_my_self(@_); # ok # but @_ is intact (including invocant)
If you ask me, I may agree this module is possibly overkill. But better code is no code. And I don't have to code anymore to get methods which work seamlessly as class methods (with the right object behavior underneath).
I first borrowed the code of find_my_self from Test::Base, but my use case turned to be pretty much simpler. Test::Base struggles to turn methods into functions by discovering the default object under the hood. In contrast, this module is only for good OO code.
Test::Base
Class::Default
Please report bugs via CPAN RT http://rt.cpan.org/NoAuth/Bugs.html?Dist=Class-Lego or mailto://bugs-Class-Lego@rt.cpan.org.
Adriano R. Ferreira, <ferreira@cpan.org>
Copyright (C) 2008 by Adriano R. Ferreira
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
To install Class::Lego, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Class::Lego
CPAN shell
perl -MCPAN -e shell install Class::Lego
For more information on module installation, please visit the detailed CPAN module installation guide.