7

简而言之,我尝试使用网络中每个实例的对象来建模网络拓扑。此外,我还有一个顶级管理器类,负责管理这些对象并执行完整性检查。文件结构看起来像这样(我省略了大部分目标文件,因为它们的结构都非常相同):

Manager.pm
Constants.pm
Classes/
  +- Machine.pm
  +- Node.pm
  +- Object.pm
  +- Switch.pm

从事 OOP 工作多年,我是代码重用等的粉丝,因此我在对象之间设置了继承,继承树(在此示例中)如下所示:

Switch  -+-> Node -+-> Object
Machine -+

所有这些对象的结构如下:

package Switch;
use parent qw(Node);

sub buildFromXML {
  ...
}
sub new {
  ...
}

# additonal methods

现在有趣的部分:

问题 1

如何在不静态输入名称的情况下确保正确加载所有这些对象?根本问题是:如果我只是require "$_" foreach glob("./Classes/*");得到许多“子程序新重新定义在”错误。我还尝试了use parent qw(-norequire Object)Module::Find以及各种组合的其他一些@INC修改,以使其简短:它不起作用。目前我正在静态导入所有使用的类,它们会自动导入它们的父类。
所以基本上我要问的是:(perl-)正确的方法是什么?
高级:能够创建更复杂的文件夹结构(因为会有很多对象)并且仍然具有继承+“自动加载”,这将非常有帮助

问题 2 -已解决

我怎样才能“分享我的进口”?我使用了几个库(我自己的,包含一些辅助函数、LibXMLScalar::Util等),我想在我的对象之间共享它们。(这背后的原因是,我可能需要为所有对象添加另一个通用库,并且很有可能会有超过 100 个对象 - 手动编辑所有对象并使用正则表达式 / 脚本执行此操作在理论上可行,但是这似乎不是最干净的解决方案)
我尝试了什么:

  • 将所有内容导入Manager.pm-> 在 Manager 包中工作 - 给我类似“未定义的子例程 &Switch::trace 调用”之类的错误
  • 在每个对象中创建一个include.pl文件和//do它- 给我同样的错误。requireuse
  • 还有一些我很遗憾不记得的东西

include.pl基本上看起来像这样:

use lib_perl;
use Scalar::Util qw(blessed);
use XML::LibXML;
use Data::Dumper;
use Error::TryCatch;
...

我再次问:正确的方法是什么?我是在使用正确的方法,只是在执行中失败了,还是应该完全改变我的结构?
为什么我当前的代码不能很好地工作并不重要,到目前为止,为这些问题提供一个正确、干净的方法就足够了:)

编辑:完全忘记 perl 版本 -_- 旁注:我无法升级 perl,因为我需要使用 5.8 的库:/

C:\> perl -version
This is perl, v5.8.8 built for MSWin32-x86-multi-thread
(with 50 registered patches, see perl -V for more detail)

Copyright 1987-2006, Larry Wall

Binary build 820 [274739] provided by ActiveState http://www.ActiveState.com
Built Jan 23 2007 15:57:46
4

3 回答 3

3

This is just a partial answer to question 2, sharing imports.

Loading a module (via use) does two things:

  1. Compiling the module and installing the contents in the namespace hierarchy (which is shared). See perldoc -f require.
  2. Calling the import sub on each loaded module. This loads some subs or constants etc. into the namespace of the caller. This is a process that the Exporter class largely hides from view. This part is important to use subs etc. without their full name, e.g. max instead of List::Util::max. See perldoc -f use.

Lets view following three modules: A, B and User.

{
   package A;
   use List::Util qw(max);
   # can use List::Util::max
   # can use max
}
{
   package User;
   # can use List::Util::max -> it is already loaded
   # cannot use max, this name is not defined in this namespace
}

Package B defines a sub load that loads a predefined list of modules and subs into the callers namespace:

{
   package B;
   sub load {
     my $package = (caller())[0]; # caller is a built-in, fetches package name

     eval qq{package $package;} . <<'FINIS' ;
       use List::Util qw(max);
       # add further modules here to load
       # you can place arbitrarily complex code in this eval string
       # to execute it in all modules that call this sub.
       # (e.g. testing and registering)
       # However, this is orthogonal to OOP.
FINIS

     if ($@) {
       # Do error handling
     }
   }
}

Inside the eval'd string, we temporarily switch into the callers package and then load the specified module. This means that the User package code now looks like this:

{
   package User;
   B::load();
   # can use List::Util::max
   # can use max
}

However, you have to make sure the load sub is already loaded itself. use B if in doubt. It might be best to execute B::load() in the BEGIN phase, before the rest of the module is compiled:

{
  package User;
  BEGIN {use B; B::load()}
  # ...
}

is equivalent to

{
  package User;
  use B;
  use List::Util qw(max);
  # ...
}

TIMTOWTDI. Although I find evaling code quite messy and dangerous, it is the way I'd pursue in this scenario (rather than doing files, which is similar but has different side effects). Manually messing with typeglobs in the package namespace is hell in comparision, and copy-pasting a list of module names is like going back to the days when there wasn't even C's preprocessor.


Edit: Import::Into

… is a CPAN module providing this functionality via an interesting method interface. Using this module, we would redefine our B package the following way:

{
  package B;
  use List::Util;   # you have to 'use' or 'require' this first, before using 'load'.
  use Import::Into; # has to be installed from CPAN first
  sub load {
    my $package = caller;
    List::Util->import::into($package, qw(max));
    # should work too: strict->import::into($package);
    # ...
  }
}

This module hides all the dirty work (evaling) from view and does method call resolution gymnastics to allow importing pragmas into other namespaces.

于 2012-09-14T10:08:15.733 回答
0

正确的惯用 Perl 方法不是总是加载一堆模块,无论是否使用;它是让每个文件都有use它直接(而不是间接)需要的那些模块。

如果事实证明每个文件都使用相同的模块集,您可以通过使用单个专用模块来使用该公共集中的所有模块,从而使事情变得更简单。

于 2013-12-01T21:17:13.193 回答
0

Import::Into 解决方案的附录

我在 Import::Into 解决方案中发现了一个似乎需要 eval() 的场景。在这种情况下,mod User 是包 B 的有效用途之一。这可能是使用 Import::Into 的人的常见场景。

规格:

  • 我创建了模块uses_exporter,带有单独的子模块,用于导入不同的模块组,例如load_generic() 和load_list_utils()。

  • load_list_utils() 中的用途是用于 List::MoreUtils 等公共模块,以及我自己的模块 list_utils_again。该本地模块还调用 load_list_utils()。如果 load_list_utils() 再次使用 list_utils_,则调用失败。

  • 我的解决方案是将 list_utils_again 的使用放入一个 eval 中,该 eval 在 $target eq 'list_utils_again' 时不会执行

于 2013-12-01T21:08:56.737 回答