我的直觉说这是一个非常糟糕的主意,但我无法确定它的具体问题。以下是使用UNIVERSAL包中的AUTOLOAD子例程的mixin / 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";
};
}
答案 0 :(得分:3)
问题:
AUTOLOAD
时,您应该创建相应的can
。UNIVERSAL::AUTOLOAD
。with
。AUTOLOAD
打破。有些是可以修复的,有些则不是。但它表明这是多么复杂和脆弱。它完全是不必要的。 with
可以导入方法,也可以只将类添加到@ISA
。
答案 1 :(得分:0)
我唯一能想到的是UNIVERSAL ::在第一次调用该方法之前可以正常工作。