【发布时间】:2010-10-23 11:33:52
【问题描述】:
对不起,Java 行话,但我怎样才能重载 Moose 构造函数?
假设我代表一个细分市场。我可以取一个起点和点,或者起点和长度,或者终点和长度。
我怎样才能允许这样的替代构造方法?
【问题讨论】:
-
你的问题让我觉得你没有读过the manual section on object construction,这里详细介绍了这一点。
对不起,Java 行话,但我怎样才能重载 Moose 构造函数?
假设我代表一个细分市场。我可以取一个起点和点,或者起点和长度,或者终点和长度。
我怎样才能允许这样的替代构造方法?
【问题讨论】:
您不需要覆盖new。 You can supply your own BUILD:
#!/usr/bin/perl
package My::Segment;
use Moose;
use namespace::autoclean;
use Carp qw( confess );
has 'start' => (is => 'ro', isa => 'Num',
predicate => 'has_start', writer => '_set_start',
);
has 'end' => (is => 'ro', isa => 'Num',
predicate => 'has_end', writer => '_set_end',
);
has 'length' => (is => 'ro', isa => 'Num',
predicate => 'has_length', writer => '_set_length',
);
sub BUILD {
my $self = shift;
$self->has_start and $self->has_end and $self->length and do {
return if $self->length == $self->end - $self->start;
confess "Inconsistent start, end and length";
};
$self->has_start and $self->has_end and do {
$self->_set_length($self->end - $self->start);
return;
};
$self->has_start and $self->has_length and do {
$self->_set_end($self->start + $self->length);
return;
};
$self->has_end and $self->has_length and do {
$self->_set_start($self->end - $self->length);
return;
};
confess "At least two of start, end or length must be supplied";
}
__PACKAGE__->meta->make_immutable;
package main;
use YAML;
my $x = My::Segment->new(start => 0, length => 3);
my $y = My::Segment->new(start => 1, end => 4);
my $z = My::Segment->new(end => 5, length => 3);
print Dump($_) for $x, $y, $z;
my $w = My::Segment->new(start => 0, end => 0, length => 1);
【讨论】:
confess。 (2) 计算缺失参数(_set_length unless has_length; etc...)。 (3) 检查 3 个参数是否一致。
Sinan 的BUILD 答案可能是最明智、最直接的解决方案。戴夫提到的使用BUILDARGS 也是一个合理的解决方案。
我觉得值得一提的是,也可以使用类型强制。给定一个类:
class LineSegment {
has [qw(startX startY endX endY)] => (
isa => 'Num',
is => 'ro',
required => 1
);
}
你可以像这样使用一组强制转换:
class_type 'LineSegment';
subtype StartLength
=> as Hashref
=> where { exists $_->{startX} && $_->{startY} && $_->{length} };
subtype EndLength
=> as Hashref
=> where { exists $_->{endX} && $_->{endY} && $_->{length} };
coerce LineSegment
=> from StartLength
=> via { my ($endX, $endY) = calc_end($_);
LineSegment->new(
startX => $_->{startX},
startY => $_->{startY},
endX => $endX,
endY => $endY,
)};
coerce LineSegment
=> from EndLength
=> via { my ($startX, $startY) = calc_start($_);
LineSegment->new(
startX => $startX,
startY => $startY,
endX => $_->{endX},
endY => $_->{endY},
)};
然后在你的代码中:
use Moose::Util::TypeConstraints;
find_type_constraint('LineSegment')->coerce({
startX => $x,
startY => $y,
length => $length
});
虽然对于这个例子来说可能有点矫枉过正,但有几次强制转换是一种优雅的解决方案。例如,如果您有一个预先存在的 LineSegment 类,您希望不向其中添加 length 属性(尽管 BUILDARGS 在那里也可以正常工作)
【讨论】: