在Perl中重载对象操作

时间:2014-11-29 12:02:08

标签: perl

我想在修改对象的方法中使用重载运算符。我也希望在不重复代码的情况下实现它。

为了说明问题,我将展示我想要做的简化版本。在我的原始代码中,add方法重载+complicated_calculation方法尝试更新对象。

add方法创建一个新的Number对象,以避免像$n + 1这样的表达式修改对象。

package Number;

use overload
    '0+' => 'get_value',
    '+' => 'add';

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

sub get_value {
    my ($self) = @_;
    return $self->{value};
}

sub set_value {
    my ($self, $value) = @_;
    $self->{value} = $value;
}

# Actual class has more attributes and the logic of addition includes branches.
sub add {
    my ($self, $other) = @_;
    print "add $other\n";
    return Number->new($self->get_value + $other);
}

sub complicated_calculation {
    my ($self) = @_;
    # Do something complicated.
    $self += 10;
}


package main;

my $n = Number->new(1);

print $n + 1 . "\n";

$n++;
print $n . "\n";

$n->complicated_calculation;
print $n . "\n";

将输出

add 1
2
add 1
2
add 10
2

我希望打印complicated_calculation方法(12)的结果,而是打印2。 complicated_calculation方法的结果设置为add方法创建的对象,而不是调用它的对象。

我可以使用complicated_calculation方法使add_in_place方法更新对象以就地添加数字,但这需要addadd_in_place中的重复代码我被教导要避免。

在实际应用程序中,Number类将具有更多属性,并且添加的代码将更长。

package Number;

use overload
    '0+' => 'get_value',
    '+' => 'add',
    '+=' => 'add_in_place',
    'fallback' => 1;

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

sub get_value {
    my ($self) = @_;
    return $self->{value};
}

sub set_value {
    my ($self, $value) = @_;
    $self->{value} = $value;
}

# Actual class has more attributes and the logic of addition includes branches.
sub add {
    my ($self, $other) = @_;
    print "add $other\n";
    return Number->new($self->get_value + $other);
}

sub add_in_place {
    my ($self, $other) = @_;
    print "add_in_place $other\n";
    $self->set_value($self->get_value + $other);
}

sub complicated_calculation {
    my ($self) = @_;
    # Do something complicated.
    $self += 10;
}


package main;

my $n = Number->new(1);

print $n + 1 . "\n";

$n++;
print $n . "\n";

$n->complicated_calculation;
print $n . "\n";

将输出

add 1
2
add_in_place 1
2
add_in_place 10
12

我觉得应该有更好的方法,并希望得到你们的一些建议。

1 个答案:

答案 0 :(得分:3)

首先,必须始终 use strictuse warnings位于您编写的每个 Perl程序文件的顶部。这尤其适用于您在寻求代码帮助时,因为它是防止错误的第一道防线,并且在困扰他人之前真的应该是您的第一手段。

这种情况正在发生,因为调用add方法来实现+=运算符,该运算符返回一个新的Number对象。这会导致$selfcomplicated_calculation的值被更改为引用 new Number对象,正确地,其值为12.但原始值为值 - 主代码中的$n - 仍然指向值为2的对象。

要使其正常运行,您可以安排complicated_calculation 返回新对象,并且调用代码会将其分配给$n。只需将该语句更改为

即可
$n = $n->complicated_calculation

会让它发挥作用。

然而,将这样的东西写成一种方法有点奇怪。 Number类中的代码应该专注于使对象行为正确,因此所有方法都应该是运算符。如果您在complicated_calculation包中编写main作为子例程,那么您可以使用

$n += 10;
print $n;

因为$n的复制将正确且透明地工作。只有在编写方法时才重新分配$self没有意义,因为它不再引用调用代码所使用的对象。

如果你真的认为complicated_calculation是一个运算符,那么它应该就地改变对象,而不是依靠overload来提供机制。如果您将其更改为

sub complicated_calculation {
  my ($self) = @_;
  $self->{value} += 10;
}

然后一切都会按预期工作。


<强>更新

我坚信你应该用add_in_place来写一切,这应该是一个私人方法,只能在课堂内部使用。

addcomplicated_calculation都可以非常简单地重写,并且不再需要编写$n = $n->complicated_calculation,因为该方法可以就地修改对象。

该模块的示例代码演示了。

package Number;

use strict;
use warnings;
use 5.010;

use overload
    '0+' => 'get_value',
    '+'  => 'add';

sub new {
  my ($class, $value) = @_;
  bless { value => $value };
}

sub get_value {
  my ($self) = @_;
  $self->{value};
}

sub set_value {
  my ($self, $value) = @_;
  $self->{value} = $value;
}

sub add {
    my ($self, $other) = @_;
    print "add $other\n";
    Number->new($self->get_value)->add_in_place($other);
}

sub add_in_place {
    my ($self, $other) = @_;
    print "add_in_place $other\n";
    $self->{value} += $other;
    $self;
}

sub complicated_calculation {
  my ($self) = @_;
  $self->add_in_place(10);
}