4

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

($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)ish 的方式声明 2 个绑定属性?


编辑。我展示了方法的代码_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

4 回答 4

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

于 2012-08-25T14:16:00.080 回答
1

当面对这种性质的东西——两个或多个属性的值一次生成——并且没有令人信服的理由创建一个小类来处理这个问题时,我通常创建一个属性,然后委托访问器来访问结果。例如:

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 和 token_start 传递给 new() 的类时。

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

于 2012-10-12T18:33:05.037 回答
1

我不知道有任何方法可以将这两个属性绑定在一起,就像您使用直接哈希分配一样。

我可能会让两个懒惰的建设者做类似的事情:

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

然后反过来构建 token_start。

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


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

我不确定问题是否清楚。在我看来,这两个值似乎属于一起,或者至少 token_start 取决于 token。我更喜欢使用$self->auth->token 这样的链接是明确的。

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

于 2012-08-20T12:13:21.550 回答
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});    
}

有没有我注意到的一些陷阱?

于 2012-08-21T07:57:44.297 回答