【发布时间】:2015-02-26 07:54:07
【问题描述】:
我的直觉说这是一个非常糟糕的主意,但我无法确定它的具体问题。下面是使用 UNIVERSAL 包中的 AUTOLOAD 子例程的 mixins/traits 的一个非常原始的实现。就XY problem 的答案而言,正确的答案是使用Moo,但与我交谈的人出于某种毫无意义的原因不想使用非核心模块,我想说服他们这种方法,而技术上可行,但这是个坏主意,所以我需要技术上的理由说明这种方法除了令人不安之外是个坏主意。
#!/usr/bin/env perl
use strict;
use warnings;
use 5.010;
{
package UNIVERSAL;
sub with {
my ($class, @mixins) = @_;
our %mixin_map;
push @{ $mixin_map{$class} }, @mixins;
}
sub AUTOLOAD {
our $AUTOLOAD;
# Never propagate DESTROY methods
return if ($AUTOLOAD =~ /::DESTROY$/);
my ($class, $method) = $AUTOLOAD =~ /(.*)::(.*)/;
my @mixins = do {
our %mixin_map;
@{ $mixin_map{$class} };
};
for my $mixin (@mixins) {
# find the mixin/trait that supports this method
if (my $sub = $mixin->can($method)) {
{ #install the mixin's method in the class
no strict "refs";
*{ "$class::$method" } = $sub;
}
# call this class's method with the original arguments
return $class->can($method)->(@_);
}
}
use Carp;
Carp::croak("could not find a method $method for class $class\nlooked in:", join ", ", @mixins);
}
}
{
package T;
T->with(qw( Init Misc ));
}
{
package A;
A->with( qw/Init Helper/ );
}
{
package Init;
sub new {
my ($class, $hParams) = @_;
return bless {}, $class;
}
}
{
package Helper;
sub foo {
my ($self) = @_;
print "foo here\n";
}
}
{
package Misc;
sub something {
my ($self) = @_;
print "and more...\n";
}
}
{
package main;
my $t = T->new;
my $a = A->new;
$a->foo;
$t->something;
eval {
$t->foo;
1;
} or do {
print "yay! calling foo on t failed\n";
};
eval {
$a->something;
1;
} or do {
print "yay! calling somehting on a failed\n";
};
}
【问题讨论】:
-
调用
with时为什么不直接导入mixins? -
当您想为此使用 UNIVERSAL::AUTOLOAD 而其他人想将其用于其他事情时会发生什么?
-
@ysth,AUTOLOAD 是 UNIVERSAL 以外的命名空间应该链接,AUTOLOAD 是 UNIVERSAL 应该包装。不幸的是,我怀疑很少有人这样做。