【问题标题】:Why shouldn't I create an AUTOLOAD subroutine in the UNIVERSAL package?为什么我不应该在 UNIVERSAL 包中创建一个 AUTOLOAD 子例程?
【发布时间】: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 应该包装。不幸的是,我怀疑很少有人这样做。

标签: perl autoload universal


【解决方案1】:

问题:

  • 安装AUTOLOAD时,应创建对应的can
  • 破坏任何现有的UNIVERSAL::AUTOLOAD
  • 向所有类添加with
  • 被大多数其他AUTOLOAD 破坏。

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 当您创建一个AUTOLOAD 时,您也创建了一个can。这是 OP 实现中的错误,而不是限制。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-14
    • 2014-12-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多