假设我有两个角色:Simple :: Tax和Real :: Tax.在测试情况下,我想使用Simple :: Tax,在生产中,我想使用Real :: Tax.做这个的最好方式是什么?我的第一个想法是使用不同版本的新方法来创建具有不同角色的对象:
#!/usr/bin/perl use warnings; { package Simple::Tax; use Moose::Role; requires 'price'; sub calculate_tax { my $self = shift; return int($self->price * 0.05); } } { package A; use Moose; use Moose::Util qw( apply_all_roles ); has price => ( is => "rw",isa => 'Int' ); #price in pennies sub new_with_simple_tax { my $class = shift; my $obj = $class->new(@_); apply_all_roles( $obj,"Simple::Tax" ); } } my $o = A->new_with_simple_tax(price => 100); print $o->calculate_tax," cents\n";
我的第二个想法是在包的主体中使用if语句来使用不同的语句:
#!/usr/bin/perl use warnings; { package Complex::Tax; use Moose::Role; requires 'price'; sub calculate_tax { my $self = shift; #pretend this is more complex return int($self->price * 0.15); } } { package Simple::Tax; use Moose::Role; requires 'price'; sub calculate_tax { my $self = shift; return int($self->price * 0.05); } } { package A; use Moose; has price => ( is => "rw",isa => 'Int' ); #price in pennies if ($ENV{TEST_A}) { with "Simple::Tax"; } else { with "Complex::Tax"; } } my $o = A->new(price => 100); print $o->calculate_tax," cents\n";
其中一个比另一个更好,它们中的任何一个都有一些可怕的东西,还有一种我还没想过的更好的方法.
解决方法
我的第一个建议是像MooseX :: Traits,然后在对象创建时指定不同的角色:
my $test = A->with_traits('Simple::Tax')->new(...); my $prod = A->with_traits('Complex::Tax')->new(...);
但是这打开了在没有应用角色的情况下创建A的大门.所以进一步思考,我认为你有一个X / Y问题.如果Simple :: Tax仅用于在测试环境中模拟Complex :: Tax,则可以执行多项操作来覆盖Complex :: Tax实现.
例如,您可以像这样定义Simple :: Tax:
package Simple::Tax; use Moose::Role; requires 'calculate_tax'; around calculate_tax => sub { int($_[1]->price * 0.05) };
然后总是使用A compose Complex :: Tax并仅在测试期间将Simple :: Tax应用于它(使用apply_all_roles).
但是,如果您在生产中需要Simple :: Tax和Complex :: Tax(而不仅仅是测试),那么最好的选择是从组合关系(确实)到委托关系(has)的重构.
package TaxCalculator::API; use Moose::Role; requires qw(calculate_tax); package SimpleTax::Calculator; use Moose; with qw(TaxCalculator::API); sub calculate_tax { ... } package ComplexTax::Calculator; use Moose; with qw(TaxCalculator::API); sub calcuate_tax { ... } package A; use Moose; has tax_calculator => ( does => 'TaxCalculator::API',handles => 'TaxCalculator::API',default => sub { ComplexTax::Calculator->new() },);
然后,如果你想覆盖它,你只需传入一个新的tax_calculator:
my $test = A->new(tax_calculator => SimpleTax::Calculator->new()); my $prod = A->new(tax_calculator => ComplexTax::Calculator->new());
因为句柄会将角色中的所有方法委托为新代理,这几乎与自己编写角色相同.
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。