加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

Perl OOP属性操作最佳实践

发布时间:2020-12-15 21:57:52 所属栏目:大数据 来源:网络整理
导读:假设以下代码: package Thing;sub new { my $this=shift; bless {@_},$this;}sub name { my $this=shift; if (@_) { $this-{_name}=shift; } return $this-{_name};} 现在假设我们已经实例化了一个对象: my $o=Thing-new();$o-name('Harold'); 够好了.我们
假设以下代码:
package Thing;
sub new {
    my $this=shift;
    bless {@_},$this;
}
sub name {
    my $this=shift;
    if (@_) {
        $this->{_name}=shift;
    }
    return $this->{_name};
}

现在假设我们已经实例化了一个对象:

my $o=Thing->new();
$o->name('Harold');

够好了.我们还可以使用以下任一方法更快地实例化相同的事情:

my $o=Thing->new(_name=>'Harold');  # poor form
my $o=Thing->new()->name('Harold');

可以肯定的是,我允许在构造函数中传递属性,以允许“友好”类更完整地创建对象.它还可以允许克隆类型运算符使用以下代码:

my $o=Thing->new(%$otherthing);  # will clone attrs if not deeper than 1 level

这一切都很好.我理解需要隐藏方法后面的属性以允许验证等.

$o->name;  # returns 'Harold'
$o->name('Fred'); # sets name to 'Fred' and returns 'Fred'

但是这不允许基于自身轻松操纵属性,例如:

$o->{_name}=~s/old/ry/;  # name is now 'Harry',but this "exposes" the attribute

一种替代方法是执行以下操作:

# Cumbersome,not syntactically sweet
my $n=$o->name;
$n=~s/old/ry/;
$o->name($n);

另一个潜力是以下方法:

sub Name :lvalue {  # note the capital 'N',not the same as name
    my $this=shift;
    return $this->{_name};
}

现在我可以做以下事情:

$o->Name=~s/old/ry/;

所以我的问题是……这是上面的“犹太洁食”吗?或者以这种方式公开属性是不好的形式?我的意思是,这样做会消除“名称”方法中可能发现的任何验证.例如,如果’name’方法强制执行大写第一个字母和小写字母,那么’Name'(大写’N’)会绕过该名称并强制该类用户在使用它时自行警告.

那么,如果’名称’左值方法不完全是“犹太教”,那么有没有确定的方法来做这些事情?

我考虑过(但是考虑到晕眩)像绑定标量这样的东西.可以肯定的是,它可能是要走的路.

此外,是否有可能有帮助的重载?

或者我应该创建替代方法(如果它甚至可以工作):

sub replace_name {
    my $this=shift;
    my $repl=shift;
    my $new=shift;
    $this->{_name}=~s/$repl/$new/;
}
...
$o->replace_name(qr/old/,'ry');

在此先感谢…并注意,我对Perl的OOP品牌不是很有经验,尽管我对OOP本身非常熟悉.

附加信息:
我想我的界面可以变得非常有创意……这是我修改过的一个想法,但我想这表明它确实没有界限:

sub name {
    my $this=shift;
    if (@_) {
        my $first=shift;
        if (ref($first) eq 'Regexp') {
            my $second=shift;
            $this->{_name}=~s/$first/$second/;
        }
        else {
            $this->{_name}=$first;
        }
    }
    return $this->{_name};
}

现在,我可以设置name属性

$o->name('Fred');

或者我可以操纵它

$o->name(qr/old/,'ry');  # name is now Harry

这仍然不允许像$o-> name.=’Jr.’这样的东西.但这并不难添加.哎呀,我可以让calllback函数传入,不是吗?

解决方法

你的第一个代码示例绝对没问题.这是编写访问器的标准方法.当然,在进行替换时这会变得很难看,最好的解决方案可能是:
$o->name($o->name =~ s/old/ry/r);

/ r标志返回替换的结果.等价的:

$o->name(do { (my $t = $o->name) =~ s/old/ry/; $t });

是的,这第二个解决方案无疑是丑陋的.但我假设访问字段比设置字段更常见.

根据您的个人风格偏好,您可以使用两种不同的方法进行获取和设置,例如: name和set_name. (我不认为get_前缀是个好主意 – 4个不必要的字符).

如果替换部分名称是您的类的核心方面,那么将其封装在特殊的substitute_name方法听起来是个好主意.否则,这只是不必要的镇流器,以及避免偶尔出现句法痛苦的糟糕权衡.

我不建议你使用左值方法,如these are experimental.

我宁愿不看(并调试)一些返回绑定标量的“聪明”代码.这可行,但对于我对这些解决方案感到满意感觉有点太脆弱了.

运算符重载对编写访问器没有帮助.特别是赋值不能在Perl中重载.

编写访问器很无聊,特别是当他们没有验证时.有些模块可以为我们处理自动生成,例如Class::Accessor.这会在您的类中添加和设置通用访问器,并根据请求添加特定访问器.例如.

package Thing;
use Class::Accessor 'antlers';  # use the Moose-ish syntax
has name => (is => 'rw');  # declare a read-write attribute

# new is autogenerated. Achtung: this takes a hashref

然后:

Thing->new({ name => 'Harold'});
# or
Thing->new->name('Harold');
# or any of the other permutations.

如果你想要一个Perl的现代对象系统,那么就有一排兼容的实现.其中功能最丰富的是Moose,并允许您向属性添加验证,类型约束,默认值等.例如.

package Thing;
use Moose; # this is now a Moose class

has first_name => (
  is => 'rw',isa => 'Str',required => 1,# must be given in constructor
  trigger => &;_update_name,# run this sub after attribute is set
);
has last_name => (
  is => 'rw',);
has name => (
  is => 'ro',# readonly
  writer => '_set_name',# but private setter
);

sub _update_name {
  my $self = shift;
  $self->_set_name(join ' ',$self->first_name,$self->last_name);
}

# accessors are normal Moose methods,which we can modify
before first_name => sub {
  my $self = shift;
  if (@_ and $_[0] !~ /^pU/) {
    Carp::croak "First name must begin with uppercase letter";
  }
};

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读