我可以将对象方法替换为不同对象上的不同方法吗?

时间:2012-07-16 19:13:42

标签: perl

通过示例更容易解释:

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()

是否可以在Perl中执行此操作?也许使用一些typeglob或coderef技巧?

我试图保持SpecialEffects界面简单。我不想开始构建对象层次结构。 sound()方法是公开的,只能稍微配置它的行为。

我已经知道你可以使用*sound = \&fizz;别名,但就我所知,这是一个全局性的东西,我希望它封装在对象中。

3 个答案:

答案 0 :(得分:5)

简单,简单,非魔法的方法是在SpecialEffects对象中存储方法名称,根据您想要发生的任何事情进行设置,并从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";
}

如果您想要更精灵,可以像方法名一样轻松地存储和使用coderefs。

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();
}

答案 1 :(得分:2)

AFAIK如果不在散列中存储引用,则无法创建每个实例的方法($o->{sound}()可以完成)。

方法绑定到类,它们对应于perl包。所以你应该对*SpecialEffects::sound = &SpecialEffects::fizz进行管理。这适用于一个类的所有对象。对不起,这不是javascript等,我自己讨厌...

(您可以动态执行一些半wizadry并创建一个包SpecialEffects::clonky(在运行时),其中只包含soundclonk子类SpecialEffects别名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" 然后重新考虑您的参考。这实际上是您不需要的层次结构,但您不必实际制作.pm文件

编辑:我不确定这是怎么做到的,但是它遵循以下几行↓如果它起作用,这是一个半优雅的解决方案(它会弄乱你的名字空间)

{{1}}

答案 2 :(得分:1)

使用Moose

package SpecialEffects;
use Moose;

has '_method' => (is => 'ro', isa => 'CodeRef');

around BUILDARGS => sub {
    my ($orig, $class, %args) = @_;
    my %map = (a => 'fizz', b => 'clonk');
    if (my $config = delete $args{config}) {
        $args{_method} = $class->can($map{$config});
    }
    return $class->$orig(%args);
};

sub sound {shift->_method->(@_)}
sub fizz  {return 'fizz';}
sub clonk {return 'clonk';}
1;

use Test::More;
use SpecialEffects;

my $o = SpecialEffects->new(config => 'a');
is($o->sound, 'fizz');
my $p = SpecialEffects->new(config => 'b');
is($p->sound, 'clonk');

done_testing;

代码积分转到#moose的omega,评论:

  

<欧米加>但不是真的“干净”:p

     

<欧米加>而不是Moosey

     

<欧米加>可能更好地应用角色