【问题标题】:How can I flexibly add data to Moose objects?如何灵活地向 Moose 对象添加数据?
【发布时间】:2010-10-22 10:48:58
【问题描述】:

我正在为驼鹿对象编写一个模块。我想允许使用此对象(或我自己......)的用户根据他/她的需要动态添加一些字段。我无法先验地定义这些字段,因为我根本不知道它们会是什么。

我目前只是添加了一个名为 extra 的 hashref 类型的字段,该字段设置为 rw,因此用户可以简单地将内容放入该哈希中:

# $obj is a ref to my Moose object    
$obj->extra()->{new_thingie}="abc123"; # adds some arbitrary stuff to the object
say $obj->extra()->{new_thingie};

这行得通。但是……这是一种常见的做法吗?还有其他(可能更优雅)的想法吗?

请注意,我不希望创建另一个扩展此模块的模块,这实际上只是为了我想添加的即时内容。

【问题讨论】:

    标签: perl moose


    【解决方案1】:

    我可能会通过原生特征来做到这一点:

    has custom_fields => (
        traits     => [qw( Hash )],
        isa        => 'HashRef',
        builder    => '_build_custom_fields',
        handles    => {
            custom_field         => 'accessor',
            has_custom_field     => 'exists',
            custom_fields        => 'keys',
            has_custom_fields    => 'count',
            delete_custom_field  => 'delete',
        },
    );
    
    sub _build_custom_fields { {} }
    

    在一个对象上你会像下面这样使用它:

    my $val = $obj->custom_field('foo');           # get field value
    $obj->custom_field('foo', 23);                 # set field to value
    
    $obj->has_custom_field('foo');                 # does a specific field exist?
    $obj->has_custom_fields;                       # are there any fields?
    
    my @names = $obj->custom_fields;               # what fields are there?
    my $value = $obj->delete_custom_field('foo');  # remove field value
    

    此类内容的常见用例是将可选的自省数据添加到异常和消息类中。

    【讨论】:

    • 如果我尝试读取(未设置)一个不存在的字段,我可以让访问器发出嘶嘶声吗?
    • 你可以用一个 around 修饰符包裹 'custom_field' 访问器,检查 args,如果 'has_custom_field' 返回 false 就发牢骚。
    【解决方案2】:

    如果您还没有使类不可变(有一个 performance penalty 不这样做,除了我担心动态更改类定义之外),您应该能够通过获取元类来做到这一点对于对象(使用$meta = $object->meta)并使用Class::MOP::Class 中的add_attribute 方法。

    #!/usr/bin/perl
    
    package My::Class;
    
    use Moose;
    use namespace::autoclean;
    
    package main;
    
    my $x = My::Class->new;
    my $meta = $x->meta;
    $meta->add_attribute(
        foo => (
            accessor => 'foo',
        )
    );
    
    $x->foo(42);
    
    print $x->foo, "\n";
    
    my $y = My::Class->new({ foo => 5 });
    print $y->foo, "\n";
    

    输出:

    42
    5

    【讨论】:

    • +1 有趣。谢谢。我通常确实将类设置为不可变(遵循 Moose 最佳实践)。
    【解决方案3】:

    如果您想将方法添加到对象而不是整个类,请查看 MooseX::SingletonMethod 之类的内容。

    例如

    use 5.012;
    use warnings;
    
    {
        package Foo;
        use MooseX::SingletonMethod;
        sub bar { 'bar' }     # method available to all objects
    }
    
    my $foo = Foo->new;
    
    $foo->add_singleton_method( baz => sub { 'baz!' } );
    
    $foo->baz;     # => baz!
    

    所以在上面的方法baz只被添加到对象$foo而不是类Foo

    嗯...我想知道我是否可以实现 MooseX::SingletonAttribute?


    以前使用MooseX::SingletonMethod的一些SO答案:

    还有这篇博文可能有用和/或感兴趣:Easy Anonymous Objects

    /I3az/

    【讨论】:

      【解决方案4】:

      即使在运行时修改类不是一个好习惯,您也可以简单地使元类可变,添加属性并再次使类不可变:

       $ref->meta->make_mutable ;
       $ref->meta->add_attribute($attr_name,%cfg) ;
       $ref->meta->make_immmutable ;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-06-16
        • 2018-09-05
        • 1970-01-01
        • 1970-01-01
        • 2023-02-15
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多