为什么不在UNIVERSAL包中创建AUTOLOAD子例程?

时间:2015-02-25 21:04:33

标签: perl autoload universal

我的直觉说这是一个非常糟糕的主意,但我无法确定它的具体问题。以下是使用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";
    };
}

2 个答案:

答案 0 :(得分:3)

问题:

  • 安装AUTOLOAD时,您应该创建相应的can
  • 打破任何现有的UNIVERSAL::AUTOLOAD
  • 向所有班级添加with
  • 被大多数其他AUTOLOAD打破。

有些是可以修复的,有些则不是。但它表明这是多么复杂和脆弱。它完全是不必要的。 with可以导入方法,也可以只将类添加到@ISA

答案 1 :(得分:0)

我唯一能想到的是UNIVERSAL ::在第一次调用该方法之前可以正常工作。