【问题标题】:Dynamically modifying methods in Moo using the published Moo API使用已发布的 Moo API 在 Moo 中动态修改方法
【发布时间】:2019-01-27 18:58:17
【问题描述】:

我正在尝试坚持已发布的API来动态修改Moo中的方法,还没有想出一个通用的解决方案。

首先,一些代码:

package R1 {
    use Moo::Role;
    sub r1 { say __PACKAGE__ }
}

package C1 {
    use Moo;
    sub c1 { say __PACKAGE__ }
}

use Scalar::Util qw[ blessed ];
use Moo::Role ();

my $c = C1->new;
Moo::Role->apply_roles_to_object( $c, 'R1' );

角色应用程序将说明一种方法的失败。

我尝试了两种方法。

第一次使用Class::Method::Modifiers

use Class::Method::Modifiers qw[ install_modifier ];
install_modifier( blessed( $c ), 
                  before => r1 =>
                  sub { say "BEFORE r1" }
                );
$c->r1;

并且工作正常:

% perl test.pl
BEFORE r1
R1

Moo 的内部_install_modifier 子程序的代码非常相似,但也执行了额外的Moo 特定动作,因此这种方法并不完全等效。

我尝试的下一个方法是直接使用$c 可用的before 修饰符,从而获得额外的Moo 特殊酱汁:

$c->can('before')->( r1 => sub { say "BEFORE r1" } );
$c->r1;

但是……

% perl test.pl
The method 'r1' is not found in the inheritance hierarchy for class C1 at [...]/lib/site_perl/5.28.0/Class/Method/Modifiers.pm line 42.
        Class::Method::Modifiers::install_modifier("C1", "before", "r1") called at /[...]/lib/site_perl/5.28.0/Moo/_Utils.pm line 44
        Moo::_Utils::_install_modifier("C1", "before", "r1", CODE(0x5590bb800360)) called at [...]/lib/site_perl/5.28.0/Moo.pm line 84
        Moo::before("r1", CODE(0x5590bb800360)) called at test.pl line 25

似乎为原始C1 类生成了修饰符,并且在应用R1 角色时未更新。以下令人震惊的黑客“修复”了:

use Import::Into;
Moo->import::into( blessed $c );

$c->can('before')->( r1 => sub { say "BEFORE r1" } );
$c->r1;

导致:

% perl test.pl
BEFORE r1
R1

那么,有没有一种方法可以仅使用已发布的Moo API 来实现我的目标?

谢谢!

【问题讨论】:

  • 这听起来像是你可能会使用元类来做的事情。
  • 为什么不使用 Moose,它为此提供了适当的 api。 Moo 就像没有元数据的 Moose,如果您开始需要它,只需使用 Moose。它们应该相当兼容,因此您不必重新开发所有内容。
  • @bytepusher 不幸的是,对于我的计算环境中的应用程序,Moose 的启动开销太高。
  • 事实上,您可以通过调用->meta 直接从 Moo 对象使用 Moose 元类,但这仅意味着开销将在您膨胀元类时而不是在编译期间。
  • 老实说,既然你排除了元类方法,如果你不能解决角色问题,我会建议一种完全不同的方法,比如事件分发,一种即时发布/订阅的形式。您无需尝试包装方法,而是更改方法以发出事件,然后系统的其他部分订阅它们想要影响的事件。见Role::EventEmitter and all the similar options listed in its SEE ALSO

标签: perl moo


【解决方案1】:

您可以通过仅应用另一个角色来修改方法(它甚至不必是 Moo::Role,除非您正在处理属性):

use Role::Tiny;
before r1 => sub { say "BEFORE r1" };

只要确保在构成 r1 方法的那个角色之后应用这个角色,或者在角色中包含一个虚拟的 sub r1 {}(如果已经存在,它将被忽略)。

【讨论】:

  • 恐怕我看不出这是如何解决问题的。这仅在定义类时有效,之后无效。我需要修改类定义之外的方法。
  • @DiabJerius 我不确定你的意思。您可以随时申请角色。你的意思是修改方法的方式是动态确定的,不能创建这样的角色来覆盖所有情况?
  • 是的,你的问题。类是动态生成的,它们的方法也是动态修改的。至于在任何时候应用角色,这在技术上并不完全正确——在一个类被实例化后你不能应用角色,见rt.cpan.org/Ticket/Display.html?id=101631
  • @DiabJerius 您绝对可以在实例化后应用角色,这就是 apply_roles_to_object 方法的全部目的。这将创建一个新类并 reblesses 对象。
  • 我认为这有点升级了,所以让我澄清一下。我的问题是关于修改类,而不是对象,我指出的 RT 链接是关于类,而不是对象。我知道角色可以应用于对象(我在示例代码中使用了 apply_roles_to_object)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多