Badger
view release on metacpan or search on metacpan
lib/Badger/Class.pm view on Meta::CPAN
will always return the relevant class for the object. If C<$self> is
an instance of C<Your::Module>, then you'll get a C<Badger::Class>
object for C<Your::Module>.
my $ym = Your::Module->new;
$ym->introspect; # I am a Your::Module instance
However, if C<$self> is an instance of a I<subclass> of C<Your::Module>, say,
C<My::Module>, then you'll get a C<Badger::Object> back for C<My::Module>
instead.
package My::Module;
use base 'Your::Module';
package main;
my $mm = My::Module->new;
$mm->introspect; # I am a My::Module instance
In this simple example it would have been just as easy to use C<ref> to find
out what kind of object we were dealing with, especially when all we're doing
is printing the class name. However, things get more interesting when we
combine that with the ability to inspect and define class variables.
Consider this base class module:
package Amplifier;
use Badger::Class
base => 'Badger::Base',
import => 'class',
get_methods => 'max_volume';
our $MAX_VOLUME = 10;
sub init {
my ($self, $config) = @_;
$self->{ volume } = 0; # start quietly
$self->{ max_volume } = $config->{ max_volume }
|| $MAX_VOLUME;
return $self;
}
The C<init()> method (see L<Badger::Base>) looks for a C<max_volume>
setting in the configuration parameters, or defaults to the C<$MAX_VOLUME>
package variable.
my $amp = Amplifer->new; # default max_volume: 10
So you're on ten here, all the way up, all the way up, all the way up,
you're on ten on your guitar. Where can you go from there? Where? Nowhere.
Exactly. What we do is, if we need that extra push over the cliff, you know
what we do?
my $amp = Amplifier->new( max_volume => 11 );
Eleven. Exactly. One louder.
So far, so good. But what if we wanted to make this the default? Sure, we
could make ten louder and make that be the top number, or we could remember to
specify the C<max_volume> parameter each time we use it. But let's assume
we're working with temperamental artistes who will be too busy worrying about
the quality of the backstage catering to think about checking their volume
settings before they go on stage.
Thankfully we didn't hard-code the maximum volume but used the C<$MAX_VOLUME>
package variable instead. We can change it directly like this:
$Amplifier::MAX_VOLUME = 11;
Or using the class L<var()> method (just to show you what the roundabout way
looks like):
Amplifier->class->var( MAX_VOLUME => 11 );
Either way has the desired effect of changing the default maximum volume
setting without having to go and edit the source code of the module.
The downside to this is that it is an all-encompassing change that will affect
all future instances of C<Amplifier> and any subclasses derived from it that
don't define their own C<max_volume> parameter explicitly.
But what if that's not what you want? What if you're playing a Jazz/Blues
festival on the Isle of Lucy, for example, or performing a musical trilogy in
D minor, the saddest of all keys? In that case you don't want to change I<all>
the amplifiers, just I<some> of them.
This is the kind of problem that is easily solved by using inheritance. Your
base class amplifier defines the default properties and behaviours for the
I<general case>, leaving subclasses to reimplement anything that needs
changing for more I<specific cases>. All the bits that don't get redefined
by a subclass are automatically inherited from the base class.
The only problem is that Perl's limited OO model only applies inheritance to
methods and not package variables. However, we can use the C<Badger::Class>
object to roll our own inheritance mechanism for package variables where
needed.
Let's look again at the relevant line from the C<init()> method where
the C<max_volume> is set:
$self->{ max_volume } = $config->{ max_volume }
|| $MAX_VOLUME;
Rather than accessing C<$MAX_VOLUME> directly, we can instead use the
class object to fetch the value of the C<$MAX_VOLUME> class variable for us.
$self->{ max_volume } = $config->{ max_volume }
|| $self->class->var('MAX_VOLUME');
This will continue to work as before for all instances of C<Amplifer>. It's a
little more long-winded and involves an extra method call or two, but it has
the benefit of working correctly with respect to inheritance. That means we
can now subclass C<Amplifier> and define a different default value for
C<$MAX_VOLUME>.
package Nigels::Amplifier;
use base 'Amplifier';
our $VOLUME = 11;
The C<init()> method will now look for the C<$MAX_VOLUME> variable in our
subclass package (C<Nigels::Amplifier>) instead of the base class
( run in 2.667 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )