如何以Mooseish方式声明2个依赖属性?

时间:2012-08-20 11:46:49

标签: perl moose moo

在我的对象构造函数中,我有声明同时初始化两个属性:

($self->{token}, $self->{token_start}) = $self->_get_authorized_token();

所以我得到了令牌,它在一个声明中一起开始。

现在我尝试移植我的模块以使用Moo(se),在这里我不知道我应该如何同时设置这两个绑定属性?一些伪代码就是这样的:

has qw/token token_start/ => (
    is  => 'rw',
    default => shift->_get_authorized_token(); 
);

但是如何在Moo(se)中声明2个绑定属性呢?


EDIT。我展示了方法_get_authorized_token的代码,也许它会带来一些想法:

sub _get_authorized_token {
    my $self = shift;
    my $postData = { 'apikey' => $self->{key} };
    my $url = $self->{base_url} . '/seller';
    my $xml = $self->_post(url => $url,
                            postdata => $postData,
                        );
    my $ref = XMLin($xml, SuppressEmpty => '' );
    my $time = $ref->{Notification_Datetime};
    my $token = $ref->{Notification_Data}{body}{token};
    return ($token, $time); 
}

4 个答案:

答案 0 :(得分:5)

一旦你最终得到两个基本链接到你总是同时设置它们的点的属性......答案通常是创建一个具有两个属性的值对象,然后将相关的方法委托给它。所以,像 -

package MyApp::TokenInfo;

use Moo;

has token => (is => 'ro', required => 1);
has token_start => (is => 'ro', required => 1);

...

package MyApp::ThingWithAToken;

use Module::Runtime qw(use_module);
use Moo;

...

has token_info => (is => 'lazy', handles => [ qw(token token_start) ]);

sub _build_token_info {
  my ($self) = @_;
  my ($token, $token_start) = $self->_get_authorized_token;

  # this is equivalent to:
  #
  #   require MyApp::TokenInfo;
  #   return MyApp::TokenInfo->new(...);
  #
  # but more concise

  return use_module('MyApp::TokenInfo')->new(
    token => $token,
    token_start => $token_start
  );
}

...

my $thing = MyApp::ThingWithAToken->new(...);

$thing->token; # calls $thing->token_info->token;
$thing->token_start; # calls $thing->token_info->token_start

因此,不需要从外部知道值对象的存在,但在内部,您仍然将两个属性绑定在一起,使您的实现将它们作为单个“事物”处理。

- mst

答案 1 :(得分:1)

我不知道如何将两个属性绑定在一起,就像使用直接哈希分配一样。

我可能有两个懒惰的构建者做类似的事情:

sub _build_token {
  my $self = shift;
  my ($t, $ts) = $self->_get_authorized_token();
  $self->token_start($ts);
  return $t
}

然后反向构建token_start。

你真正想要的是让令牌/ token_start成为他们自己对象的一部分。这样你可以保证两者都适当地设置在一起。


  

我仍然会有2个依赖属性,我也不能将它们分开。或者重点在哪里?

我不确定问题的重点是什么。这两个值在我看来属于一起,或者至少token_start依赖于令牌。我更愿意使用$self->auth->token以便链接清晰。

如果您想跳过对“auth”对象的引用,只需使用handles

答案 2 :(得分:1)

当遇到这种性质的东西时 - 两个或多个属性的值一次生成 - 并且没有令人信服的理由创建一个小类来处理它,我通常创建一个属性然后委托访问者来访问结果。 e.g:

has _token_info => (
    traits => ['Hash'],
    is => 'ro',
    isa => 'HashRef',
    builder => '_build__token_info',
    handles => {
        token => [ get => 'token' ],
        token_start => [ get => 'token_start' ]
    },
);

sub _build__token_info {

    # ... whatever needs to be done to get $token{,_start}
    return { token => $token, token_start => $token_start };
}

这样,当有人第一次访问token()或token_start()时,会生成并传递令牌和起始值。

请注意,这种方法通常最适用于在类中私有构建或设置的值,而不是在构建将类传递令牌和token_start到new()的类时。

另见http://whitepointstarllc.com/2012/05/simulating-multiple-lazy-attributes/

答案 3 :(得分:0)

我自己也有点想法。也许不使用返回值的方法,我应该使用setter方法,它返回一个(如果有的话)值,但设置2?像这样:

sub _set_authorized_token {
    my $self = shift;
    my $postData = { 'apikey' => $self->{key} };
    my $url = $self->{base_url} . '/seller';
    my $xml = $self->_post(url => $url,
                            postdata => $postData,
                        );
    my $ref = XMLin($xml, SuppressEmpty => '' );
    $self->{token_start} = $ref->{Notification_Datetime};
    $self->{token} = $ref->{Notification_Data}{body}{token};
    return ($self->{token});    
}

我注意到了一些陷阱吗?