首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Perl:如何从基类导入子例程?

Perl:如何从基类导入子例程?
EN

Stack Overflow用户
提问于 2012-10-20 00:59:24
回答 3查看 2.3K关注 0票数 6

我有一个名为Foo::Base的基类,我需要继承它的方法,比如'new‘,并在作用域中导入一些子例程的名称:

代码语言:javascript
复制
package Foo::Base;

sub new { ... }

sub import {
    no strict 'refs';

    my $caller = caller;

    *{"${caller}::my_sub"} = sub { 1 };
}

1;

因此,我需要在我的第二个类Foo::Child中使用这个基类:

代码语言:javascript
复制
use base 'Foo::Base';

..。它适用于继承,但它不会在作用域中导入“my_sub”。我可以添加字符串

代码语言:javascript
复制
use Foo::Base;

它很有帮助,但我不想写这样的东西:

代码语言:javascript
复制
use base 'Foo::Base';
use Foo::Base;

这看起来有点奇怪..。对于这个问题有什么建议吗?

EN

回答 3

Stack Overflow用户

发布于 2012-10-20 02:30:06

首先,您正在尝试从父类导入方法...出于某种原因。也许你误解了面向对象的工作原理。你不需要这样做。只需将继承的方法调用为方法,除非这些方法做了一些奇怪的事情,否则它会工作得很好。

为此,你可以做到..。

代码语言:javascript
复制
use base 'Foo::Base';
use Foo::Base;

你正确地注意到它看起来有点奇怪。因为这有点奇怪。

最好的做法是重新设计类,而不是导出函数,或者将函数拆分到它们自己的模块中,或者使它们成为类方法。如果它们确实与类相关,那么就让它们成为类方法。

代码语言:javascript
复制
use base 'Foo::Base';

Foo::Base->some_function_that_used_to_be_exported;

这消除了接口不匹配,并且作为一个额外的好处,子类可以像任何其他方法一样覆盖类方法行为。

代码语言:javascript
复制
package Bar;

use base 'Foo::Base';

# override
sub some_function_that_used_to_be_exported {
    my($class, @args) = @_;

    ...do something extra maybe...

    $class->SUPER::some_function_that_used_to_be_exported(@args);

    ...and maybe something else...
}

如果您无法控制基类,您仍然可以通过编写一个子类来将导出的函数转换为方法,从而使接口保持正常。

代码语言:javascript
复制
package SaneFoo;

use base 'Foo::Base';

# For each function exported by Foo::Base, create a wrapper class
# method which throws away the first argument (the class name) and
# calls the function.
for my $name (@Foo::Base::EXPORT, @Foo::Base::EXPORT_OK) {
    my $function = Foo::Base->can($name);
    *{$name} = sub {
        my $class = shift;
        return $function->(@_);
    };
}
票数 14
EN

Stack Overflow用户

发布于 2012-10-20 01:41:47

在编写use base时,您将使用base模块的功能。你把你想要作为基类的模块的参数传递给它。

在OO IS-A关系中,不需要导入。使用OO模式调用方法:$object_or_class->method_name( @args )。有时这意味着你不关心调用者是谁,就像这样:

代码语言:javascript
复制
sub inherited_util {
    my ( undef, @args ) = @_;
    ... 
}

代码语言:javascript
复制
sub inherited2 { 
    shift;
    ...
}

但是,如果您希望使用在基本模块中定义的实用程序,并从该模块中定义的类行为继承,那么这正是两个use语句所表明的。

不过,如果您想在模块中使用两种不同类型的行为,最好将实用程序类型的东西拆分到各自的模块中,并在两个模块中使用它。无论哪种方式,显式的行为通常都比隐式的好。

但是,我以前使用过这个模式:

代码语言:javascript
复制
sub import { 
    shift;
    my ( $inherit_flag ) = @_;
    my $inherit 
        = lc( $inherit_flag ) ne 'inherit' ? 0
        : shift() && !!shift()             ? 1
        :                                    0
        ;
    if ( $inherit ) { 
        no strict 'refs';
        push @{caller().'::ISA'}, __PACKAGE__;
        ...
    }
    ...
}

这样,我就可以使用显式绑定的用法进行一次调用。

代码语言:javascript
复制
use UtilityParent inherit => 1, qw<normal args>;
票数 6
EN

Stack Overflow用户

发布于 2012-10-20 05:29:42

为了更多地澄清“但是为什么Foo::Child中的导入不起作用?”的问题,下面是一个程序,它将阐明实际发生的事情:

代码语言:javascript
复制
use Foo::Child;
print my_sub(); # Really? Yes, really!
print Foo::Child::my_sub()," done\n";

并将其添加到Foo::Base::import()例程中:

代码语言:javascript
复制
print "Caller is $caller\n";

代码语言:javascript
复制
Caller is main
1
Undefined subroutine &Foo::Child::my_sub called at foo_user.pl line 4.

我们看到Foo::Base报告,让我们知道调用者是谁:它是主程序!我们看到'1',这证明main::my_sub现在存在,然后失败,因为导入到了错误的命名空间。

为什么会这样呢?Base的import不会被Foo::Child中的任何东西调用;它是由主程序在加载模块的过程中调用的。如果您真的真的想强制subs导入Foo::Child,而不是方法,那么您需要显式地在Foo::Child本身中实现它。“use Foo::base”可以做到这一点,因为这会使导入以Foo::Child作为调用者来执行;如果你坚持导入,但双重使用会让你退出太多,你可以在“use base”之后立即调用Foo::Base:: import ()。

尽管如此,我更喜欢Schwern的类方法,并且我推荐这种替代方法。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/12978971

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档