加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

perl – 如何处理:由于循环导入,Moo :: Role的`before`修饰符被

发布时间:2020-12-15 23:35:25 所属栏目:大数据 来源:网络整理
导读:使用Moo :: Role,我发现循环导入会无声地阻止执行my方法的修饰符. 我在MyRole.pm中有一个Moo :: Role: package MyRole;use Moo::Role;use MyB;requires 'the_method';before the_method = sub { die 'This has been correctly executed'; };1; …… MyA.pm
使用Moo :: Role,我发现循环导入会无声地阻止执行my方法的修饰符.

我在MyRole.pm中有一个Moo :: Role:

package MyRole;
use Moo::Role;
use MyB;
requires 'the_method';
before the_method => sub { die 'This has been correctly executed'; };
1;

…… MyA.pm中的消费者:

package MyA;
use Moo;
with ( 'MyRole' );
sub the_method { die; }
1;

..和MyB.pm中的另一个:

package MyB;
use Moo;
with ( 'MyRole' );
sub the_method { die 'The code should have died before this point'; }
1;

当我运行这个script.pl:

#!/usr/bin/env perl
package main;
use MyA;
use MyB;
MyB->new()->the_method();

…我得到的代码应该在MyB.pm第4行的这一点之前就已经死了.但是我希望看到它已经在MyRole.pm第5行正确执行了.

我认为这个问题是由循环进口引起的.如果我在script.pl中切换use语句的顺序或者我更改了使用MyB,它就会消失;在MyRole.pm中是the_method中的一个要求.

这种行为有望吗?如果是这样,在无法避免循环进口的情况下处理它的最佳方法是什么?

我可以解决这个问题,但是无意中触发(特别是因为它在函数之前导致,它通常包含检查代码,会被静默跳过).

(我正在使用Moo版本2.003004.显然使用MyB;在MyRole.pm中这是多余的,但只有在我简化了这个repro示例的代码之后.)

解决方法

循环导入可能会变得相当棘手,但行为一致.关键点是:

>使用Some :: Module的行为类似于BEGIN {require Some :: Module;一些:: Module-> import}
>加载模块时,将对其进行编译和执行.在解析周围代码期间执行BEGIN块.
>每个模块只需要一次.如果再次需要,则忽略该要求.

知道这一点,我们可以将您的四个文件合并为一个文件,其中包含BEGIN块中的所需文件.

让我们从您的主文件开始:

use MyA;
use MyB;
MyB->new()->the_method();

我们可以将使用转换为BEGIN {require …}并包含MyA内容.为清楚起见,我将忽略MyA和MyB上的任何 – >导入调用,因为它们在这种情况下不相关.

BEGIN { # use MyA;
  package MyA;
  use Moo;
  with ( 'MyRole' );
  sub the_method { die; }
}
BEGIN { # use MyB;
  require MyB;
}
MyB->new()->the_method();

with(‘MyRole’)也需要MyRole,我们可以明确说明:

...
  require MyRole;
  with( 'MyRole ');

那么让我们扩展一下:

BEGIN { # use MyA;
  package MyA;
  use Moo;
  { # require MyRole;
    package MyRole;
    use Moo::Role;
    use MyB;
    requires 'the_method';
    before the_method => sub { die 'This has been correctly executed'; };
  }
  with ( 'MyRole' );
  sub the_method { die; }
}
BEGIN { # use MyB;
  require MyB;
}
MyB->new()->the_method();

然后我们可以扩展使用MyB,同时将MyB与(‘MyRole’)扩展为require:

BEGIN { # use MyA;
  package MyA;
  use Moo;
  { # require MyRole;
    package MyRole;
    use Moo::Role;
    BEGIN { # use MyB;
      package MyB;
      use Moo;
      require MyRole;
      with ( 'MyRole' );
      sub the_method { die 'The code should have died before this point'; }
    }
    requires 'the_method';
    before the_method => sub { die 'This has been correctly executed'; };
  }
  with ( 'MyRole' );
  sub the_method { die; }
}
BEGIN { # use MyB;
  require MyB;
}
MyB->new()->the_method();

在MyB中我们有一个需要MyRole,但是已经需要该模块.因此,这没有做任何事情.在执行期间,MyRole只包含以下内容:

package MyRole;
use Moo::Role;

所以角色是空的.要求’the_method’;在the_method =>之前此时尚未编译子{…}.

因此,MyB组成一个空角色,这不会影响the_method.

如何避免这种情况?避免在这些情况下使用通常是有帮助的,因为在初始化当前模块之前中断解析.这导致不直观的行为.

当您使用的模块只是类而不影响源代码的解析方式时(例如通过导入子例程),您通常可以将需求推迟到运行时.不仅是执行顶级代码的模块的运行时间,还包括主应用程序的运行时.这意味着将您的需求粘贴到需要使用导入类的子例程中.由于即使已经导入了所需的模块,require仍然有一些开销,你可以保护require状态$require_once = require Some :: Module.这样,require就没有运行时开销.

通常:您可以通过在模块的顶级代码中进行尽可能少的初始化来避免许多问题.更喜欢懒惰并推迟初始化.另一方面,这种懒惰也会使您的系统更具动态性和可预测性:很难说出已经发生了什么初始化.

更一般地说,要认真思考你的设计.为什么需要这种循环依赖?您应该决定坚持使用高级代码依赖于低级代码的分层架构,或者使用依赖性反转,其中低级代码依赖于高级接口.混合两者将导致可怕的纠结混乱(展览A:这个问题).

我确实理解一些数据模型必然具有共同递归类.在这种情况下,通过将相互依赖的类放在单个文件中来手动整理订单可能是最清楚的.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读