在我的对象构造函数中,我有声明同时初始化两个属性:
($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);
}
答案 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});
}
我注意到了一些陷阱吗?