Perl 这是为什么;“非常糟糕的做法”;在使用Moose时覆盖新的?

Perl 这是为什么;“非常糟糕的做法”;在使用Moose时覆盖新的?,perl,constructor,overriding,inline,moose,Perl,Constructor,Overriding,Inline,Moose,从页面: 重写new是一种非常糟糕的做法。相反,您应该使用BUILD或BUILDARGS方法来做同样的事情。当您重写new时,当您的类是不可变的时,Moose就不能再内联构造函数了 我的问题是,为什么这被认为是非常糟糕的做法? 我认为“内联”构造函数只是意味着构造函数与类定义在同一个包中。如果这是真的,这难道不意味着如果您在该包中重写了new,该构造函数仍将被视为“内联”构造函数吗?如果我错了,请纠正我。我不完全理解构造函数“内联”的含义 我遇到这个问题的原因是因为我正在创建一个脚本来构建对象

从页面:

重写
new
是一种非常糟糕的做法。相反,您应该使用
BUILD
BUILDARGS
方法来做同样的事情。当您重写
new
时,当您的类是不可变的时,Moose就不能再内联构造函数了

我的问题是,为什么这被认为是非常糟糕的做法?

我认为“内联”构造函数只是意味着构造函数与类定义在同一个包中。如果这是真的,这难道不意味着如果您在该包中重写了
new
,该构造函数仍将被视为“内联”构造函数吗?如果我错了,请纠正我。我不完全理解构造函数“内联”的含义


我遇到这个问题的原因是因为我正在创建一个脚本来构建对象列表。如果用户试图创建一个与列表中相同的新对象,我想停止
Moose
创建一个新对象,并返回对现有对象的引用

创建新对象时,我希望将其推送到列表中并返回新对象。当试图创建现有对象时,应返回现有对象,而不是将其推送到列表中

# Some pseudo code showing the logic of what I attempted
around BUILDARGS => sub {
    my ($orig, $self, %args) = @_;

    # loop through objects in list
    for my $object (@list) {

        # if $args used to define the new object
        # match the arguments of any object in the list
        # return the matched object

        # I want this to directly return the object
        # and skip the call to BUILD
    }

    return $self->orig(
        # Insert args here
    );
};

sub BUILD {
    my ($self) = @_;

    # I don't want this call to happen if the object already existed
    push @list, $self;
}   
创建新对象时,我尝试使用
BUILD
在创建后将其推送到列表中。问题是,当我试图创建一个现有对象并使用
BUILDARGS
返回现有对象时,似乎没有阻止
Moose
调用
BUILD
,该调用试图将对象推到列表上

我能够绕过这个问题的唯一方法是重写
new
,让它返回现有对象而不创建新对象

# Some pseudo code showing the overridden constructor
sub new {
    my ($class, %args) = @_;

    # loop through objects in list
    for my $object (@list) {

        # if $args used to define the new object
        # match the arguments of any object in the list
        # return the matched object
    }

    # Build the object
    my $self = bless {
        # Insert args here
    }, $class;

    # Add the object to the list
    push @list, $object;
}

重写
new
是可行的,但是如果真的是这样一个可怕的想法,正如
Moose
文档所建议的那样,还有更好的方法吗?

在对子例程进行排列时,意味着在调用点复制其代码,而不是插入子例程调用。这要快得多,因为它避免了在堆栈上收集参数和任何局部变量以及调用和返回操作的开销。如果类没有被声明为不可变的,这是不可能的,因为任何变异都可能意味着构造函数必须更改,因此不能再插入到行中

我发现重写
new
时存在另一个问题,而不是
Moose
无法插入构造函数。我发现,如果覆盖
new
,它会阻止
Moose
在调用构造函数时将属性设置为“必需”


例如,如果覆盖
new
,并使用如下属性定义一个类:

有'var'=>(is=>'rw',isa=>'Str',=>必需=>1)

Moose
将允许您创建此类的新实例,而无需为
var
传递值


我为我想做的事情找到了一个可行的解决方案,至少我认为它是可行的。我真的不需要内嵌构造函数的速度(我没有创建数百万个对象),但我需要“需要”属性的能力。如果您使用Moose提供的
around
功能,您基本上可以覆盖new,而不会阻止
Moose
能够“要求”属性

例:

这种方法仍然会警告您无法创建内嵌构造函数,但至少它会正常工作


注意:添加
\uuuu包\uuuuu->meta->make\u不可变('inline\u构造函数'=>0)到包的末尾将取消此警告。

我确实声明包不可变,但需要添加
inline\u构造函数=>0
。是否有任何方法可以在不完全覆盖
new
的情况下实现我想要的功能?或者,如果这是不可能的,有没有办法确保被覆盖的
仍然是“内联的”@TJWRONA2992:我建议您看看,它添加了一个
实例
方法,该方法的行为与
new
相同,但如果以前使用相同的参数调用过它,它将返回一个以前创建的对象,这看起来确实像一个潜在的解决方案;但是,它不是ActivePerl标准发行版的一部分。我工作的地方网络安全非常严格,我们无法下载任何东西,因此我无法从cpan获得任何模块。即使使用cpan命令安装模块也会失败。没有非核心模块有没有办法做到这一点?@tjwrona1992:这是一个愚蠢的规则。您可以下载发行版并从中安装它,或者您也不允许使用USB驱动器?你可以在家里把资料打印出来然后输入?!嗯,我可以得到模块,我只需要通过它,并得到批准,然后他们需要作出一个工作订单,并安装它自己。。。然后,任何想使用我的代码的人都需要这样做,所以这真的是一个麻烦。我想如果我有源代码,我可以将它保存在与模块相同的文件夹中,并以人们可以使用的方式进行打包。我已经做了一些事情,但这似乎是不必要的。
around new => sub {
    my ($orig, $class, %args) = @_;

    # loop through objects in list
    for my $object (@list) {

        # if $args used to define the new object
        # match the arguments of any object in the list
        # return the matched object without calling new
    }

    # Create a new object
    my $self = $class->orig(%args);

    # Add it to the list of objects.
    push @list, $self;
};