为什么在创建新的 Perl 模块时不简单地使用 __PACKAGE__ 呢?

Ske*_*key 7 perl

我希望我知道参考或文档告诉我这是在 Perl 中创建新对象的最佳方法:

sub new {
   my $package = shift;
   my $class = ref($package) || $package
Run Code Online (Sandbox Code Playgroud)

这就是我多年来创建对象的方式,但现在我想知道为什么要麻烦?除了一些边缘情况,为什么不简单地执行以下操作:

sub new {
    shift; # get rid of the object or package name
    my $class = __PACKAGE__;
Run Code Online (Sandbox Code Playgroud)

当没有特殊原因试图检测“新”方法被调用的命名空间时,简单地使用 __PACKAGE__ 是否有任何问题?

mob*_*mob 13

遗产

# classA.pm 
package ClassA;
sub new { $pkg = ref($_[0]) || $_[0] ; bless { foo => 42 }, $pkg }

# classB.pm
package ClassB;
use parent 'ClassA';
sub foo { ... }

# main.pl
use ClassA;
use ClassB;
$B = ClassB->new();
print $B->foo();
Run Code Online (Sandbox Code Playgroud)

在此示例中,ClassB从 继承方法ClassA,包括其构造函数。但是我们仍然希望将对象标识为属于ClassB,因此构造函数 inClassA必须尊重传递给其构造函数的引用的名称。

$B = bless { ClassA->new(), "ClassB" };   # or
$B = bless { ClassB->new(), "ClassA" };
Run Code Online (Sandbox Code Playgroud)

或在ClassB.

package ClassB;
use parent 'ClassA';
sub new { bless { ClassA::new(@_), __PACAKGE__ } }
Run Code Online (Sandbox Code Playgroud)


bri*_*foy 6

类名已经是构造函数的第一个参数,为什么不使用它呢?你不需要伸手去拿任何东西,如果你认为情况比你最初想象的更复杂,你就没有人为地造成减速。这段代码可以在继承或不继承的情况下工作:

sub new { 
    my( $class, @args ) = @_;
    ...
    bless {...}, $class;
    }
Run Code Online (Sandbox Code Playgroud)

考虑一下您编写的任何程序,如果代码情况发生变化,您必须更改多少。也许您添加了一些您并不立即需要的额外步骤,但是当您意识到您确实需要这些情况时,这可以使代码保持灵活性。在你的情况下,你实际上必须做额外的工作来忽略 Perl 专门提供的 invocant 参数来告诉你哪个类正在尝试创建一个新对象。

例如,您创建了自己的类,它对您有用并且可以很好地完成它的工作。它工作得非常好,以至于您分享它并且其他人使用它,并且他们大多对它感到满意一段时间,直到他们需要这个小小的修改。他们应该能够对您的模块进行子类化(因此,不对它或对您进行任何更改)以扩展或覆盖方法。但是,您的代码不允许这样做,因为您以更严格的方式祝福该对象,而没有增加任何好处。


您问题中的第一段代码也使用了第一个参数,但这并不是构造函数的真正处方。它通过允许已经存在的对象创建一个新对象来做一件额外的事情。在ref从对象提取祝福包的名称和用途,对于新对象。

我不是特别喜欢这种创建新对象的方式,我认为界面可能会让人们感到困惑。在现有对象上调用 new 时会发生什么?你克隆现有的吗?如果是这样,还有更好的名称,例如clone(Ruby 使用dup)。你得到一个完全新鲜的物体吗?如果是这样,你为什么要通过一个现有的对象来得到一个与它完全无关的对象?

曾几何时,许多面向对象的 Perl 示例都显示了相同类型的构造函数,并将其复制并粘贴到许多地方。但从那以后我们学到了很多。也许你有一个很好的答案,为什么你会允许这样做,但到目前为止我还没有听到一个令人信服的答案。