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"; } };