Perl:将Coderef分配给没有符号表黑客的方法

时间:2011-08-10 13:47:49

标签: perl oop class object methods

在以前关于Class :: Struct vs Object :: Accessor的查询的基础上,我想找到从对象构造函数中的coderefs分配包subs的最佳方法。嘿,我有OO属性,所以让我们来看看方法:)

请注意,我说'最好'的方式。我已经找到了两种方法,它们似乎也是类似问题中的流行答案,但它们分别需要规避严格和警告。

请注意,该示例使类用户能够在构造时覆盖格式化方法。可怜的例子,但你明白了。为了先发制人地反驳OO纯粹主义者,这在我的特定设计中更有意义,而不是让类用户使用自己的重写版本对其进行子类化。

以下是我的两次尝试:

use 5.014;
use autodie;
use strict;
use warnings;

package Account {
    use base 'Object::Accessor';

    sub new {
        my ($type, %args) = @_;

        my $self = bless { }, $type;
        $self->mk_accessors(qw(
            first_name last_name age_in_years activated
        ));

        $self->first_name(  $args{first_name  } // 'Default First Name'  );
        $self->last_name(   $args{last_name   } // 'Default Last Name'   );
        $self->age_in_years($args{age_in_years} // 'Default Age in Years');
        $self->activated(   $args{activated   } // 'Default Activated'   );

        {
            # Stop skim reading and look here!

            no warnings 'once';
            *formatted = $args{formatted} || sub {
                return 'Default formatting routine';
            };
        }

        return $self;
    }
}

my $account = Account->new;
say $account->formatted;

# Output: Default formatting routine

另一个:

use 5.014;
use autodie;
use strict;
use warnings;

package Account {
    use base 'Object::Accessor';

    sub new {
        my ($type, %args) = @_;

        my $self = bless { }, $type;
        $self->mk_accessors(qw(
            first_name last_name age_in_years activated
        ));

        $self->first_name(  $args{first_name  } // 'Default First Name'  );
        $self->last_name(   $args{last_name   } // 'Default Last Name'   );
        $self->age_in_years($args{age_in_years} // 'Default Age in Years');
        $self->activated(   $args{activated   } // 'Default Activated'   );

        {
            # Stop skim reading and look here!

            no strict 'refs';
            *{'formatted'} = $args{formatted} || sub {
                return 'Default formatting routine';
            };
        }

        return $self;
    }
}

my $account = Account->new;
say $account->formatted;

# Output: Default formatting routine

前一种方法应该在没有警告的情况下工作,但它并不是因为Perl认为我没有在任何地方使用它并在运行时将其标记为警告。后者在没有规避严格的情况下是行不通的,那应该是怎样的:)

我正在做的就是为用户提供的coderef分配一个方法。应该有一种简单的方法。

请注意,定义调用用户定义的coderef的新子节点也不起作用,因为无法从中访问词法%args。

虽然我们分配包变量,但它不能做sub。

避免从严格和警告的花园中逃脱,这是最干净的方法是什么?地狱,有可能使用我不知道的Object :: Accessor模块来做到这一点。

3 个答案:

答案 0 :(得分:2)

如果你想'格式化'是一个可自定义的每个对象的方法,并且没有覆盖每个构造函数调用的整个包/类(即所有对象)的方法(分配给包子会做),你可以将subref分配给对象“私有”密钥,并从普通方法调用它:

sub new { 
    my ($type, %args) = @_;
...
    $self->{_formatted} = $args{formatted} // sub { 'default' };
}

sub formatted {
    my ($self) = shift;
    return $self->{_formatted}->($self,@_);
}

答案 1 :(得分:1)

它可以存储在$ self:

$$self{formatted} = sub { ... };

或者放入词汇:

my $formatted = sub { 1; }; # Dummy sub for initial assignment
sub new
{
    ...
    $formatted = sub { ... };
}

sub formatted { $formatted->(@_) }

或者只是忍受没有严格的'参考'。只要它像你上面那样在一个小的,包含良好的块中,我从未认为它是邪恶的。或者更确切地说,它是邪恶的,但却是一个小的,包含良好的。

答案 2 :(得分:1)

strict 制作,可以针对您想要执行您不想意外执行的操作的特定代码将其关闭。特别是,如果你这样做,就像你在做的那样,在尽可能小的范围内。它的工作方式与pragma在C中的工作方式相同。它允许您“破坏规则”,而不会在没有“规则”的情况下容易受到无数问题的影响。

但是,如果您不想关闭任何“规则”,那么您应该查看Symbol。具体来说,qualify_to_ref将返回对符号的引用,以便在将其转换回符号(见下文)时不会触发警报。

因此,对于您提供的代码,您可以这样做:

*{ Symbol::qualify_to_ref( 'formatted' ) } 
    =  $args{formatted} 
    || sub { return 'Default formatting routine'; }
    ;