Perl OOP属性操作最佳实践
假设以下代码:
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的现代对象系统,那么就有一排兼容的实现.其中功能最丰富的是 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"; } }; (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |