More easily explained with an example:
my $o = SpecialEffects->new( "config" => 'a' );
my $p = SpecialEffects->new( "config" => 'b' );
$o->sound(); # aliased to fizz(); same as $o->fizz()
$p->sound(); # aliased to clonk(); same as $p->clonk()
Is it possible to do this in Perl? Perhaps using some typeglob or coderef trickery?
I'm trying to keep the SpecialEffects
interface simple. I'd prefer not to start building an object hierarchy. The sound()
method is what's exposed, only its behaviour can be configured slightly.
I already know that you can alias with *sound = \&fizz;
but that is a global thing as far as I know, and I'd like it encapsulated in the object.
The simple, easy, non-wizardry way is to just store a method name in your SpecialEffects
object, set it according to whatever you want to happen, and call it from sound()
.
package SpecialEffects;
sub new {
my $type = shift;
my %options = @_;
my $self = {};
bless $self, $type;
if($options{config} eq 'a') {
$self->{sound_method} = 'fizz';
} elsif($options{config} eq 'b') {
$self->{sound_method} = 'clonk';
}
return $self;
}
sub sound {
my $self = shift;
my $method_name = $self->{sound_method};
$self->$method_name();
}
sub fizz {
print "fizz\n";
}
sub clonk {
print "clonk\n";
}
You can store and use coderefs about as easily as method names, if you want to be more wizardly.
package SpecialEffects;
sub new {
my $type = shift;
my %options = @_;
my $self = {};
bless $self, $type;
if($options{config} eq 'a') {
$self->{sound_code} = $self->can('fizz');
} elsif($options{config} eq 'b') {
$self->{sound_code} = $self->can('clonk');
}
return $self;
}
sub sound {
my $self = shift;
my $code = $self->{sound_code};
$self->$code();
}
AFAIK you can't create per-instance methods without storing references in a hash ($o->{sound}()
can be done).
Methods are bound to classes, which correspond to perl packages. So you should alialise *SpecialEffects::sound = &SpecialEffects::fizz
. This applies to all objects of one class. Sorry this is not javascript etc, I hate this myself...
(You could do some semi-wizadry and create a package SpecialEffects::clonky
on the fly (during runtime), which just includes the sound
alias for clonk
and subclasses SpecialEffects
. Then rebless your reference. This is effectively your unwanted hierarchy, but you don't have to actually make the .pm files
EDIT: I'm not sure how exactly this is done, but it goes along these lines ↓ If it works, this is a semi-elegant solution (it will mess up your name space)
sub whatSoundShallIMake {
my ($self, $sound) = @_;
no strict 'refs';
my $newPackageName = "SpecialEffects::make$sound";
*{"$newPackageName\::sound"} = &{"SpecialEffects::$sound"}; # make the alias
@{"$newPackageName\::ISA"} = qw(SpecialEffects); # subclass
return bless $self, $newPackageName; # rebless
}
say ref $o;
# prints "SpecialEffects"
$o = whatSoundShallIMake($o, "fizz");
say ref $0;
# prints "SpecialEffects::makefizz"
)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With