Badger::Prototype
package Badger::Example; use base 'Badger::Prototype'; sub greeting { my $self = shift; # get prototype object if called as a class method $self = $self->prototype() unless ref $self; # continue as normal, now $self is an object if (@_) { # set greeting if called with args return ($self->{ greeting } = shift); } else { # otherwise get greeting return $self->{ greeting }; } }
This module is a subclass of Badger::Base that additionally provides the prototype() method. It is used as a base class for modules that have methods that can be called as either class or object methods.
# object method my $object = Badger::Example->new(); $object->greeting('Hello World'); # class method Badger::Example->greeting('Hello World');
The prototype() method returns a singleton object instance which can be used as a default object by methods that have been called as class methods.
Here's an example of a greeting()
method that can be called
with an argument to set a greeting message:
$object->greeting('Hello World');
Or without any arguments to get the current message:
print $object->greeting; # Hello World
As well as being called as an object method, we want to be able to call it as a class method:
Badger::Example->greeting('Hello World'); print Badger::Example->greeting(); # Hello World
Here's what the greeting()
method looks like.
package Badger::Example; use base 'Badger::Prototype'; sub greeting { my $self = shift; # get prototype object if called as a class method $self = $self->prototype() unless ref $self; # continue as normal, now $self is an object if (@_) { # set greeting if called with args return ($self->{ greeting } = shift); } else { # otherwise get greeting return $self->{ greeting }; } }
We use ref $self
to determine if greeting()
has
been called as an object method ($self
contains an object
reference) or as a class method ($self
contains the class
name, in this case Badger::Example
). In the latter case, we
call prototype() as a class method
(remember, $self
contains the Badger::Example
class name at this point) to return a prototype object instance which we
then store back into $self
.
# get prototype object if called as a class method $self = $self->prototype() unless ref $self;
For the rest of the method we can continue as if called as an object
method because $self
now contains a
Badger::Example
object either way.
Note that the prototype object reference is stored in the
$PROTOTYPE
variable in the package of the calling object's
class. So if you call prototype on a Badger::Example::One
object that is subclassed from Badger::Prototype
then the
prototype object will be stored in the
$Badger::Example::One::PROTOTYPE
package variable.
Constructor method to create a prototype object and cache it in the
$PROTOTYPE
package variable for subsequent use. This is
usually called from inside methods that can operate as class or object
methods, as shown in the earlier example.
sub example { my $self = shift; # upgrade $self to an object when called as a class method $self = $self->prototype() unless ref $self; # ...code follows... }
If you prefer a more succint idiom and aren't too worried about calling the prototype method unneccessarily, then you can write it like this:
sub greeting { my $self = shift->prototype; # ...code follows... }
If any arguments are passed to the prototype()
method then
it forces a new prototype object to be created, replacing any existing
one cached in the $PROTOTYPE
package variable. The arguments
are forwarded to the new()
constructor method called to
create the object.
If a single undefined value is passed as an argument then any existing
prototype is released by setting the $PROTOTYPE
package
variable to undef
. The existing prototype is then returned,
or undef if there was no prototype defined.
Andy Wardley http://wardley.org/