将Moose风格属性访问器添加到你的Perl类中

坦白说,为现成的Perl类编写属性访问器是重复且不太有趣的。当然,你可以使用 MooseClass::Accessor 来减轻负担,但有时你希望手动实现解决方案,无需依赖项。

一个典型的类

下面的Point类有两个属性(x,y),并且为每个属性都编写了get/set方法,采用纯Perl面向对象风格。

package Point;

sub new {
    my ($class, $x, $y) = @_;
    my $self = {
        x => $x,
        y => $y,
    };
    return bless $self, $class;
}

sub get_x {
    return $_[0]->{x};
}

sub set_x {
    return $_[0]->{x} = $_[1];
}

sub get_y {
    return $_[0]->{y};
}

sub set_y {
    return $_[0]->{y} = $_[1];
}

1;

另一种方法

上周Rob Hoelz写了一篇关于Perl类型全局变量的有趣的文章,我们可以使用类型全局变量来帮助我们的Point类属性访问器。这是更新后的类

package Point;

my @attributes;
BEGIN {
    @attributes = qw/x y/;
    no strict 'refs';
    for my $accessor ( @attributes ) {
        *{$accessor} = sub {
            @_ > 1 ? $_[0]->{$accessor} = $_[1] : $_[0]->{$accessor} };
    }
}

sub new {
    my ($class, $args) = @_;
    my $self = bless {}, $class;
    for my $key ( @attributes ) {
        $self->{$key} = $args->{$key} if exists $args->{$key}
    }
    return $self;
}

1;

删除了单独的get/set访问器,取而代之的是BEGIN块。该块关闭了严格引用(仅对块有效)并为每个属性创建一个匿名get/set子例程的类型全局引用。构造函数已被更新为接受一个参数哈希引用($args)并设置任何在$args中找到的属性值。

这种方法的优点是,添加额外属性只需简单地将属性名添加到@attributes中,而原始Point类需要添加两个新方法并更新构造函数方法才能添加新属性。此外,这种方法还支持Moose风格语法:当提供参数时设置属性值,否则获取它。例如

$point->x; #get x
$point->x(5); #set x to 5

虽然这种方法确实提供了比纯Perl面向对象更快的可扩展性和更简洁的语法,但它也是有限的。例如,在不添加一些丑陋的if-else代码的情况下很难添加属性特定的行为。因此,它可能最适合涉及简单类和许多属性的场景。

来源

感谢David Golden,他令人惊叹的HTTP::Tiny源代码启发了这篇文章。


这篇文章最初发表在PerlTricks.com

标签

David Farrell

David是一位专业程序员,他经常推文博客关于代码和编程艺术。

浏览他们的文章

反馈

这篇文章有什么问题吗?请通过在GitHub上打开问题或拉取请求来帮助我们。