如何处理 Moose 中的嘲讽角色? [英] How to handle mocking roles in Moose?

查看:64
本文介绍了如何处理 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 中的嘲讽角色?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆