本文介绍了如何处理 Moose 中的嘲讽角色?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有两个角色:Simple::Tax 和 Real::Tax.在测试情况下,我想使用 Simple::Tax,而在生产中,我想使用 Real::Tax.做这个的最好方式是什么?我的第一个想法是使用不同版本的 new 方法来创建具有不同角色的对象:

#!/usr/bin/perl使用警告;{包简单::税;使用 Moose::Role;需要价格";子计算税{我的 $self = shift;return int($self->price * 0.05);}}{套餐A;使用驼鹿;使用 Moose::Util qw( apply_all_roles );有价格 =>( is => "rw", isa => 'Int' );#以便士为单位的价格子 new_with_simple_tax {我的 $class = shift;我的 $obj = $class->new(@_);apply_all_roles( $obj, "Simple::Tax" );}}我的 $o = A->new_with_simple_tax(price => 100);打印 $o->calculate_tax, " cents\n";

我的第二个想法是在包体中使用 if 语句来使用不同的 with 语句:

#!/usr/bin/perl使用警告;{包 Complex::Tax;使用 Moose::Role;需要价格";子计算税{我的 $self = shift;#假装这更复杂return int($self->price * 0.15);}}{包简单::税;使用 Moose::Role;需要价格";子计算税{我的 $self = shift;return int($self->price * 0.05);}}{套餐A;使用驼鹿;有价格 =>( is => "rw", isa => 'Int' );#以便士为单位的价格如果 ($ENV{TEST_A}) {使用简单::税";} 别的 {使用复杂::税收";}}我的$o = A->新(价格=> 100);打印 $o->calculate_tax, " cents\n";

其中一个比另一个更好吗,它们中的任何一个都有什么可怕的地方,还有我还没有想到的更好的方法.

解决方案

我的第一个建议是类似于 MooseX::Traits 的内容,然后在创建对象时指定不同的角色:

my $test = A->with_traits('Simple::Tax')->new(...);my $prod = A->with_traits('Complex::Tax')->new(...);

但这打开了创建A而没有应用either角色的大门.因此,进一步考虑一下,我认为您遇到了 X/Y 问题.如果 Simple::Tax 仅用于在测试环境中模拟 Complex::Tax,您可以做一些事情来覆盖 Complex::Tax 实现.>

例如,您可以像这样定义 Simple::Tax:

package Simple::Tax;使用 Moose::Role;需要calculate_tax";围绕calculate_tax =>sub { int($_[1]->price * 0.05) };

然后总是让 A compose Complex::Tax 并仅在测试期间对其应用 Simple::Tax(使用 apply_all_roles).

但是,如果您在生产中(而不仅仅是为了测试)需要 Simple::Tax 和 Complex::Tax,那么您最好的选择是从组合关系(does)重构为委托关系(has).

 包 TaxCalculator::API;使用 Moose::Role;需要 qw(calculate_tax);包 SimpleTax::Calculator;使用驼鹿;与 qw(TaxCalculator::API);子计算税{ ... }包 ComplexTax::Calculator;使用驼鹿;与 qw(TaxCalculator::API);子计算税{ ... }套餐A;使用驼鹿;有 tax_calculator =>(确实=>'税收计算器::API',句柄 =>'税收计算器::API',默认=>sub { ComplexTax::Calculator->new() },);

然后如果你想覆盖它,你只需传入一个新的tax_calculator:

my $test = A->new(tax_calculator => SimpleTax::Calculator->new());我的 $prod = A->new(tax_calculator => ComplexTax::Calculator->new());

因为 handles 会将角色中的所有方法委托为新代理,这实际上与自己组成角色相同.

Say that I have two roles: Simple::Tax and Real::Tax. In testing situations, I want to use Simple::Tax, and in production, I want to use Real::Tax. What is the best way to do this? My first thought was to use different versions of the new method to create objects with different roles:

#!/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";

My second thought was to use an if statement in the body of package to use different with statements:

#!/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";

Is one of these better than the other, is there something horrible about either of them, and is there a better way I haven't thought of yet.

解决方案

My first suggestion would be something like MooseX::Traits and then specify the different roles at object creation:

my $test = A->with_traits('Simple::Tax')->new(...);

my $prod = A->with_traits('Complex::Tax')->new(...);

But this opens the door to an A being created without either Role being applied. So thinking about it further, I think you've got an X/Y problem. If Simple::Tax is only ever used to mock up Complex::Tax in a test environment you can do several things to override the Complex::Tax implementation.

For example you could just define Simple::Tax like so:

package Simple::Tax; 
use Moose::Role;

requires 'calculate_tax';
around calculate_tax => sub { int($_[1]->price * 0.05) };

Then always have A compose Complex::Tax and apply Simple::Tax to it only during tests (using apply_all_roles).

If however you need Simple::Tax and Complex::Tax both in production (and not simply for testing) your best bet is refactor from a composition relationship (does) to a delegation relationship (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() },
 );

Then if you want to override it you simply pass in a new tax_calculator:

my $test = A->new(tax_calculator => SimpleTax::Calculator->new());

my $prod = A->new(tax_calculator => ComplexTax::Calculator->new());

Because handles will delegate all of the methods from the role as new proxies this is practically identical to having composed the role yourself.

这篇关于如何处理 Moose 中的嘲讽角色?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-16 22:37