Once more unto the breach, dear friends, once more;
In my last D&D post I managed to get my generic 'OpenDoorsOnA' role working with my 'Fighter' class but I was left wondering if I would make better sense to make 'Exceptional' Strength a role then only use it the rare time that a 'Fighter' character has 18 strength????
Whenever I see the phrase 'conditionally loads the needed class' in the spec documentation it sends shivers down my spine. Then again anyone who as used DBI has used conditional loading of classes but most times I see silly shortcuts like this
my $class_ver = "17_1"
my $class = "Dingus::Bungus_$class_ver";
my $instance;
eval {
use $class;
$instance= $class->new();
}
rather than the tried and true method full inner and out handles found in the DBI's architecture.
So the task is to get the 'Exceptional' Strength in the 'Fighter' Class when the Strength is 18.
First I ripped out the 'Exceptional' parts from my Fighter class and moved them in to and 'ExceptionalStrength' role;
package ExceptionalStrength;
use Moose::Role;
has 'exceptional' =>(
is =>'rw',
isa =>'Int',
);
with
'OpenDoorsOnA', => {
-alias =>{open_door_on_a=>'normal_open_doors_on_a'},
-excludes=>'open_door_on_a' };
sub open_door_on_a {
my $self = shift;
return $self->normal_open_doors_on_a()
if ($self->strength() < 18);
given ($self->exceptional){
when (0){
return 5;
};
when ([1..50]){
return 3;
};
when ([51..99]){
return 4;
};
};
}
Now that is is out of my 'Fighter' class I have to find some some way to get in back into an instance of my Fighter? I do not want to enter into the Bean anti-patten land by creating a 'new' for 'Fighter' that calls the 'SUPER->New' of 'Character' and fortunately in Moose we have the 'BUILD' sub.
This sub fires just after an object is created but we still have the original '@_' to get any attributes that may of been passed in and use them in our build process
Fortunately Moose also has a way we can bring a role into a class instance via Moose's build in MOP support or if you prefer Meta support.
There is much talk that is is some sort of Majick going on behind the curtain but this is unfair it is just a sound and well though out framework that uses standard perl.
Anyway on with our little story. What we have to do is tell the 'MOP' that if a 'Fighter' has strength 18 he or she should get the 'ExceptionalStrength' role.
So here we go in my 'Fighter' class;
sub BUILD {
my $self = shift;
my ($attr) = @_;
return
if ($self->strength() < 18);
ExceptionalStrength->meta->apply($self);
$self->exceptional($attr->{exceptional});
if ($self->exceptional() >90 and $self->exceptional()<=99){
OpenBarredDoorsOnA1->meta->apply($self);
}
elsif ($self->exceptional() ==0 ){
OpenBarredDoorsOnA2->meta->apply($self);
}
};
So on BUILD I simply return if my strength is less than 18 or now that I know my Strength is 18 I use the 'meta->apply' method of the 'ExceptionalStrength' role to place that role in my instantiated 'Fighter' Class.
Next I should have the 'exceptional' value passed in on the '@_' and so I set my instance value with it that and then I do another check on the 'exceptional' attribute to see if I can get one of the 'OpenBarredDoorsOn' roles as well.
So in the end my little test like this
my $str = Fighter->new({name=>'Sir Cumferace',strength=>18,exceptional=>91});
print $str->name()."\n";
print "strength=".$str->strength();
print " (".$str->exceptional().")\n"
if($str->can('exceptional'));
print "opens a door on a ". $str->open_door_on_a()."\n";
print "opens a barred door on a ".$str->open_barred_doors_on_a
if($str->can('open_barred_doors_on_a'));
give me this
Sir Cumferace
strength=18 (91)
opens a door on a 4
opens a barred door on a 1
Ok onwards and upwards but there a few more things I would like to look into like having the 'ExceptionalStrength' role give us the 'OpenBarredDoorsOnA' role and also set the attribute when it is built but that is for another day I think.
Leave a comment