【问题标题】:Is it possible to make an attribute configuration dependent on another attribute in Moo?是否可以使属性配置依赖于 Moo 中的另一个属性?
【发布时间】:2015-11-16 14:12:42
【问题描述】:

我已阅读各种教程和 Moo 文档,但我找不到任何描述我想做的事情的内容。

我想要做的是如下所示:

has 'status' => (
  is  => 'ro',
  isa => Enum[qw(pending waiting completed)],
);

has 'someother' => (
  is       => is_status() eq 'waiting'   ? 'ro' : 'rw',
  required => is_status() eq 'completed' ? 1    : 0,
  isa      => Str,
  lazy     => 1,
);

如果我对这个想法不太了解,我将如何根据另一个属性的值来制作属性“ro”或“rw”以及是否需要?

注意,枚举来自Type::Tiny

【问题讨论】:

  • 简短的回答是“否”,因为属性配置是“静态的”(即适用于整个类),与特定对象的值无关。
  • 所以,我可能不得不重写属性的 setter 方法并处理检查以确定是否可以修改值。但我不知道如何处理所需的部分......
  • 使用判断是否完成的getter?
  • 如果调用者从不尝试访问该属性,我将如何强制调用该属性 getter?
  • 当我重新阅读 moo 文档时,我突然想到,也许我可以使用 BUILDARGS 动态添加一个属性......或者这会是一个可憎的事情吗?

标签: perl moo


【解决方案1】:

问问自己为什么要这样做。您正在处理对象。这些是应用了一组逻辑的数据。该逻辑在类中进行了描述,对象是应用了类逻辑的数据实例。

如果有一个属性(即数据)可以应用两种不同的逻辑,它是否仍然属于同一类?毕竟,属性是否可变是一个非常明确的规则。

所以你真的有两个不同的课程。一种someother 属性是只读的,另一种是可更改的。

在 Moo(和 Moose)中,有几种方法可以构建它。

  • 实现 Foo::Static 和 Foo::Dynamic(或 Changeable 或其他),它们都是 Foo 的子类,并且只有一个属性发生变化
  • 实现 Foo 并实现子类
  • 实现 Foo 和改变someother 行为的角色,并将其应用到构造函数中。 Moo::Role 继承自 Role::Tiny

这是使用角色的方法的示例。

package Foo;
use Moo;
use Role::Tiny ();

has 'status' => ( is => 'ro', );

has 'someother' => (
    is      => 'ro',
    lazy    => 1,
);


sub BUILD {
  my ( $self) = @_;

  Role::Tiny->apply_roles_to_object($self, 'Foo::Role::Someother::Dynamic')
    if $self->status eq 'foo';
}

package Foo::Role::Someother::Dynamic;
use Moo::Role;

has '+someother' => ( is => 'rw', required => 1 );

package main;
use strict;
use warnings;
use Data::Printer;

# ...

首先,我们将创建一个具有动态someother 的对象。

my $foo = Foo->new( status => 'foo', someother => 'foo' );
p $foo;
$foo->someother('asdf');
print $foo->someother;

__END__
Foo__WITH__Foo::Role::Someother::Dynamic  {
    Parents       Role::Tiny::_COMPOSABLE::Foo::Role::Someother::Dynamic, Foo
    Linear @ISA   Foo__WITH__Foo::Role::Someother::Dynamic, Role::Tiny::_COMPOSABLE::Foo::Role::Someother::Dynamic, Role::Tiny::_COMPOSABLE::Foo::Role::Someother::Dynamic::_BASE, Foo, Moo::Object
    public methods (0)
    private methods (0)
    internals: {
        someother   "foo",
        status      "foo"
    }
}
asdf

如您所见,这是可行的。现在让我们制作一个静态的。

my $bar = Foo->new( status => 'bar', someother => 'bar' );
p $bar;
$bar->someother('asdf');

__END__
Foo  {
    Parents       Moo::Object
    public methods (4) : BUILD, new, someother, status
    private methods (0)
    internals: {
        someother   "bar",
        status      "bar"
    }
}
Usage: Foo::someother(self) at /home/julien/code/scratch.pl line 327.

哎呀。一个警告。不像 Moose 那样是一个很好的“只读”异常,但我想这已经是最好的了。

但是,这对required 属性没有帮助。您可以创建一个不带someotherFoo->new( status => 'foo' ),它仍然可以正常运行。

因此,您可能希望满足于子类方法或使用角色并构建factory class

【讨论】:

  • 谢谢。这给了我另一个探索的途径。
猜你喜欢
  • 1970-01-01
  • 2020-10-19
  • 2020-05-17
  • 1970-01-01
  • 2010-11-21
  • 1970-01-01
  • 1970-01-01
  • 2011-07-22
  • 1970-01-01
相关资源
最近更新 更多