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-解決済み

どうすれば「輸入品を共有」できますか?私はいくつかのライブラリ(私自身、いくつかのヘルパー関数、、などを含むLibXML)を使用Scalar::Utilしており、それらをオブジェクト間で共有したいと考えています。(その背後にある理由は、すべてのオブジェクトに別の共通ライブラリを追加する必要がある可能性があり、100をはるかに超えるオブジェクトが存在する可能性が高いです-それらすべてを手動で編集して正規表現/スクリプトでそれを行うのは理論的には機能しますが、それは利用可能な最もクリーンなソリューションのようには思えません)
私が試したこと:

  • すべてをインポートManager.pm->Managerパッケージ内で動作-「未定義のサブルーチン&Switch::traceが呼び出されました」などのエラーが表示されます
  • include.plファイルを作成し、do//すべてのオブジェクト内にファイルを作成しrequireますuse-同じエラーが発生します。
  • 悲しいことに覚えていないもの

include.pl基本的には次のようになります。

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

もう一度私は尋ねます:それをする正しい方法は何ですか?私は正しいアプローチを使用していて、実行に失敗しただけですか、それとも構造を完全に変更する必要がありますか?
私の現在のコードがそれほどうまく機能しない理由はそれほど重要ではありません。これらの問題に対して正しくクリーンなアプローチを提供するだけで十分です:)

編集:perlバージョンを完全に忘れた-_-補足:5.8でスタックしているライブラリが必要なため、perlをアップグレードできません:/

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

これは、質問2のインポートの共有に対する部分的な回答にすぎません。

(経由で)モジュールをロードすると、次のuse2つのことが行われます。

  1. モジュールをコンパイルし、名前空間階層(共有されている)にコンテンツをインストールします。を参照してくださいperldoc -f require
  2. importロードされた各モジュールのサブを呼び出します。これにより、呼び出し元の名前空間にいくつかのサブまたは定数などがロードされます。これは、Exporterクラスが主に非表示にするプロセスです。maxこの部分は、たとえば。の代わりに、フルネームなしで潜水艦などを使用することが重要ですList::Util::max。を参照してくださいperldoc -f use

次の3つのモジュールを表示してみましょう:A、、。BUser

{
   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
}

パッケージは、モジュールとサブの事前定義されたリストを呼び出し元の名前空間にロードするBサブを定義します。load

{
   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
     }
   }
}

'd文字列内で、eval一時的に呼び出し元パッケージに切り替えてから、指定されたモジュールをロードします。これは、Userパッケージコードが次のようになったことを意味します。

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

ただし、loadサブがすでにロードされていることを確認する必要があります。use B疑わしい場合。モジュールの残りの部分をコンパイルする前にB::load()、フェーズで実行するのが最適な場合があります。BEGIN

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

と同等です

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

TIMTOWTDI。evalingコードは非常に厄介で危険だと思いますが、このシナリオで追求する方法です(ファイルをingするのではなく、似doていますが副作用が異なります)。パッケージの名前空間でtypeglobsを手動で操作することは、比較すると非常に困難です。モジュール名のリストをコピーして貼り付けることは、Cのプリプロセッサさえなかった時代に戻るようなものです。


編集:Import::Into

…は、興味深いメソッドインターフェイスを介してこの機能を提供するCPANモジュールです。このモジュールを使用して、Bパッケージを次のように再定義します。

{
  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);
    # ...
  }
}

このモジュールは、すべてのダーティワーク(evaling)をビューから隠し、メソッド呼び出し解決体操を実行して、プラグマを他の名前空間にインポートできるようにします。

于 2012-09-14T10:08:15.733 に答える
0

Import::IntoSolutionの補遺

Import :: Intoソリューション内からeval()が必要と思われるシナリオを見つけました。このシナリオでは、mod Userは効果的にパッケージBからの使用の中にあります。これは、Import::Intoを使用する人々にとって一般的なシナリオである可能性があります。

詳細:

  • load_generic()やload_list_utils()など、モジュールのさまざまなグループをインポートするための個別のサブを持つモジュールuses_exporterを作成しました。

  • load_list_utils()での使用は、List :: MoreUtilsのようなパブリックmodと、私自身のモジュールlist_utils_againです。そのローカルモジュールはload_list_utils()も呼び出します。load_list_utils()がlist_utils_againを使用する場合、呼び出しは失敗します。

  • 私の解決策は、list_utils_againの使用を、$ targeteq'list_utils_again'の場合に実行されないevalに入れることでした。

于 2013-12-01T21:08:56.737 に答える
0

これを行うための正しい慣用的なPerlの方法は、使用されているかどうかに関係なく、常にモジュールを大量にロードすることではありません。use直接(間接的にではなく)必要なモジュールをすべてのファイルに含めることです。

すべてのファイルが同じモジュールのセットを使用していることが判明した場合は、その共通のセット内のすべてのモジュールを使用する単一の専用モジュールを使用することで、作業を簡単にすることができます。

于 2013-12-01T21:17:13.193 に答える