为什么这是非常糟糕的做法"使用Moose时覆盖新的?

时间:2015-06-26 14:55:59

标签: perl constructor override inline moose

来自Moose::Manual::BestPractices页面:

  

覆盖new非常糟糕的做法。相反,您应该使用BUILDBUILDARGS方法来执行相同的操作。当您重写new时,当您的类不稳定时,Moose不能再内联构造函数。

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

我认为"内联"构造函数只是意味着构造函数是在与类相同的包中定义的。如果这是真的,那么这意味着如果你在该包中覆盖new,那么构造函数仍将被视为" inline"构造函数?如果我错了,请纠正我。我并不完全理解构造函数对内联的意义。"

我遇到这个问题的原因是因为我正在创建一个构建对象列表的脚本。如果用户尝试创建一个与列表中的对象相同的新对象,我想阻止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文档似乎暗示的那样,有更好的方法吗?

2 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:1)

我发现覆盖new的另一个问题,除了Moose无法内嵌构造函数。我发现如果你覆盖new,它会阻止Moose在调用构造函数时使属性“必需”。

例如,如果您覆盖new并定义具有如下属性的类:

has 'var' => (is => 'rw', isa => 'Str', => required => 1);

Moose将允许您创建此类的新实例,而不向其传递var的值。

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

例如:

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

这种方法仍然会向您发出关于无法创建内嵌构造函数的警告,但至少它会正常运行。

注意:在您的软件包末尾添加__PACKAGE__->meta->make_immutable('inline_constructor' => 0);将取消此警告。