从Perl中的类构造函数调用基础构造函数的正确方法是什么?
我见过这样的语法:
my $class = shift;
my $a = shift;
my $b = shift;
my $self = $class->SUPER::new($a, $b);
return $self;
这是对的吗?如果我们有几个父类怎么办?例如,像这样的类:
package Gamma;
use base Alpha;
use base Beta;
sub new
{
# Call base constructors...
}
1;
答案 0 :(得分:32)
如果你的所有构造函数都在调用父构造函数(如在你的例子中,你根本不需要编写一个。只需将其保留并调用父元素;你只需要确保该对象被祝福成为正确的类型:
package Parent;
use strict;
use warnings;
sub new
{
my ($class, @args) = @_;
# do something with @args
return bless {}, $class;
}
1;
如果您使用上面的代码并使用Child
声明了use parent 'Parent';
类,则Parent构造函数将正确构造子代。
如果您需要在Child中添加一些属性,那么您拥有的内容基本上是正确的:
package Child;
use strict;
use warnings;
use parent 'Parent';
sub new
{
my ($class, @args) = @_;
# possibly call Parent->new(@args) first
my $self = $class->SUPER::new(@args);
# do something else with @args
# no need to rebless $self, if the Parent already blessed properly
return $self;
}
1;
但是,当你将多个继承带入组合时,你需要决定在每一步中做正确的事情。这意味着每个类的自定义构造函数决定如何将Parent1和Parent2的属性合并到子项中,然后最终将生成的对象保存到Child类中。这种复杂性是多重继承是一个糟糕的设计选择的众多原因之一。您是否考虑过重新设计对象层次结构,可能是将某些属性转换为角色?此外,您可能希望使用对象框架来完成一些繁忙的工作,例如Moose。现在很少需要编写自定义构造函数。
(最后,您应该避免使用变量$a
和$b
;它们在Perl中的处理方式不同,因为它们是排序函数和其他一些内置函数中使用的变量。)
答案 1 :(得分:19)
这个问题就是为什么有些人建议你不要在new
方法中做任何有趣的事情。
new
期望创建并返回一个有福的引用,很难让一个系统处理来自不同父类的同一对象的两次。
更清洁的选项是拥有一个只创建对象的新方法,并调用另一个可以设置对象的方法。第二种方法的行为方式允许调用多个父方法。有效地new
是你的分配器,而另一种方法是你的构造函数。
package Mother;
use strict;
use warnings;
sub new {
my ($class, @args) = @_;
my $self = bless {}, $class;
return $self->_init(@args);
}
sub _init {
my ($self, @args) = @_;
# do something
return $self;
}
package Father;
use strict;
use warnings;
sub new {
my ($class, @args) = @_;
my $self = bless {}, $class;
return $self->_init(@args);
}
sub _init {
my ($self, @args) = @_;
# do something else
return $self;
}
package Child;
use strict;
use warnings;
use base qw(Mother Father);
sub _init {
my ($self, @args) = @_;
# do any thing that needs to be done before calling base classes
$self->Mother::_init(@args); # Call Mother::_init explicitly, SUPER::_init would also call Mother::_init
$self->Father::_init(@args); # Call Father::_init explicitly, SUPER::_init would NOT call Father::_init
# do any thing that needs to be done after calling base classes
return $self;
}
以太是对使用多重继承可能发现的并发症的正确看法。您仍然需要知道Father::_init
不会覆盖Mother::_init
开始做出的任何决定。从那里开始调试只会变得更加复杂和困难。
我会推荐Moose的推荐,它的界面包括一个更好的对象创建和初始化分离,而不是上面通常只能工作的例子。
答案 2 :(得分:5)
使用多重继承时,默认方法解析顺序为低于标准。我强烈建议您添加
use mro 'c3';
到模块,并使用
调用链中的下一个构造函数sub new {
my ($class) = @_;
return $class->next::method(@_);
}
Alpha和Beta必须这样做才能发挥作用。
参考:mro