【问题标题】:How can I make a module that imports many modules for the user?如何制作一个为用户导入许多模块的模块?
【发布时间】:2011-04-01 06:51:19
【问题描述】:

我在 Perl 中实现了一个相当复杂的数据结构。这已分为大约 20 个类。基本上,任何时候你想使用这些类之一,你都需要使用它们。

现在,如果有人想使用这种数据结构,他们需要做如下事情:

use Component::Root;
use Component::Foo;
use Component::Bar;
use Component::Baz;
use Component::Flib;
use Component::Zen;
use Component::Zen::Foo;
use Component::Zen::Bar;
use Component::Zen::Baz;
... # 15 more of these...
use Component::Last;

能够操纵它的所有部分。我怎样才能为用户编写一个模块,所以他们所要做的就是

use Component;

要导入所有其他模块吗?

在这种特殊情况下,模块都是类并且没有导出。

【问题讨论】:

标签: perl module


【解决方案1】:

如果这些只是类(即当您use 它们时它们不会导出任何函数或变量),那么真正重要的是它们已被加载。

只需创建Component.pm:

package Component;

our $VERSION = '1.00';

use Component::Root;
use Component::Foo;
use Component::Bar;
use Component::Baz;
use Component::Flib;
use Component::Zen;
use Component::Zen::Foo;
use Component::Zen::Bar;
use Component::Zen::Baz;
... # 15 more of these...
use Component::Last;

1; # Package return value

你不需要Exporter 或类似的东西。

然而,与其拥有一个只是 use 语句的模块,不如将这些 use 语句放入根节点的类或创建数据结构的模块中可能更有意义。也就是说,人们会想说:

use Component::Root;
my $root = Component::Root->new(...);

use Component qw(build_structure);
my $root = build_structure(...);

取决于您的数据结构通常是如何创建的。人们写起来可能有点混乱:

use Component;
my $root = Component::Root->new(...);

但这真的取决于你的 API 是什么样的。如果人们可能会在许多课程上调用new,那么use Component 可能是要走的路。

【讨论】:

  • 您的第二个想法是完全正确的:用户将在某个时间点或另一个点创建大多数 Component::* 对象的新实例,至少在最初或者如果他们想要手动执行操作。
  • 您的第二个想法对于创建好对象很重要。通过在对象自包含的对象的根节点中包含所有必需的支持模块。这是封装的一个重要部分。如果我必须调用两个模块,一个是 new 模块,一个是其他模块来引入我的所有引用,那么该对象不是自包含的。
  • 1) 我不敢相信这是一个真正的问题。我很想认为这只是为了积分而创建的。 -- 当然你只是把它们打包在一起。 -- 2) 关于第二个,这取决于可能需要加载多少个类,如果只是一个,则将它们全部加载到基类中。如果需要使用的不止一个,则将它们全部打包在一个模块中,并从其他每个包中调用该模块。
  • @vol7ron:很抱歉您有这种感觉,但您对 #1 的看法是错误的。在 Perl 中做事的“最佳”方式往往不是最明显的方式。与其盲目地四处走动,不如询问并达成共识。
  • @Robert P: 是的,对诚实评论的一个很好的回复。我永远不会直接责备一个真正问他们不知道的事情的人。但是,以您看似高深的理解力,人们会认为您不会问这么基本的问题。
【解决方案2】:
  • 您可以对所有 Exporters 的软件包使用 export_to_level 方法。

    MyPackage->export_to_level($where_to_export, $package, @what_to_export);
    
  • 您也可以只导出您导入的所有符号。

    use PackageA qw<Huey Dewey Louie>;
    ...
    our @ISA = qw<Exporter>; #inherit Exporter
    our @EXPORT = qw<Huey Dewey Louie>;
    
  • 但是,如果您不想导出任何符号,而只想加载模块,那么只需包含上面的那些use 语句,过程中的任何包都可以将它们实例化为类,假设它们都是 OO 模块。

    如果它们已经加载成功,它们将存在于%INC和符号表中。

【讨论】:

  • 如果您需要导出某些内容,这些是个好主意,但在这种情况下,没有什么可导出的。
【解决方案3】:

Moose::Exporter 似乎可以这样做,尽管您的所有其他模块也必须使用它。

Component:

Moose::Exporter->setup_import_methods(
    also => [qw/Component::Root Component::..*/],
);

【讨论】:

    【解决方案4】:

    如果模块不导出任何内容并且没有导入方法(与 cjm 的答案相同的要求),您只需要加载模块而不导入:

    package Component;
    
    our $VERSION = '1.00';
    
    require Component::Root;
    require Component::Foo;
    require Component::Bar;
    require Component::Baz;
    require Component::Flib;
    require Component::Zen;
    require Component::Zen::Foo;
    require Component::Zen::Bar;
    require Component::Zen::Baz;
    ... # 15 more of these...
    require Component::Last;
    
    1; # Package return value
    

    模块的用户只会这样做:

    require Component;
    

    但是,如果某些模块会导出,则必须调用它们的 import 方法。因此,您在 Component 模块中添加了一个 import 方法,该方法将调用它们:

    sub import
    {
        Component::Root->import;
        Component::Foo->import;
        ...
    }
    

    所以模块用户必须use它:

    use Component;
    

    请注意,如果导入的模块必须在导入器的上下文中插入符号,您可能必须使用一些其他技巧。例如,看看POE's import 是如何做到的。

    【讨论】:

      【解决方案5】:

      Modern::Perl 模块自诩“通过一个命令启用 Modern Perl 的所有功能”,该命令在哪里

      use Modern::Perl;
      

      这些功能是

      目前,这只启用 strict 和 warnings pragma,以及 Perl 5.10 中可用的所有功能。它还启用了 C3 方法解析顺序;有关解释,请参阅 perldoc mro。

      一行代码就这么多了,根据perlmod documentation,这完全等价于

      BEGIN { require Module; import Module; }
      

      考虑 Modern::Perl 的 implementation:

      package Modern::Perl;
      
      our $VERSION = '1.03';
      
      use 5.010_000;
      
      use strict;
      use warnings;
      
      use mro     ();
      use feature ();
      
      sub import {
          warnings->import();
          strict->import();
          feature->import( ':5.10' );
          mro::set_mro( scalar caller(), 'c3' );
      }
      
      1; # End of Modern::Perl
      

      为了适应您的情况,从您的顶级模块 use 加载您想要加载的所有其他模块,并从 MyTopLevelModule::import 调用它们的导入(如果有)。

      请注意,您不一定需要复制

      use 5.010_000;
      

      进入MyTopLevelModule.pm,但那是个好主意!根据use documentation

      在特殊的use VERSION 形式中,VERSION 可以是正小数,例如5.006,将与$] 进行比较,或者是@ 形式的v-string 987654337@,将与$^V(又名$PERL_VERSION)进行比较。如果 VERSION 大于当前 Perl 解释器的版本,则会引发异常; Perl 不会尝试解析文件的其余部分。与require 比较,它可以在运行时进行类似的检查。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-12-10
        • 1970-01-01
        • 1970-01-01
        • 2014-01-12
        • 2018-03-01
        • 2018-04-23
        • 2018-06-12
        • 1970-01-01
        相关资源
        最近更新 更多