如何处理穆斯的模拟角色?

时间:2012-01-25 15:58:49

标签: perl moose

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

#!/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语句来使用不同的with语句:

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

其中一个比另一个好,是否有一些可怕的东西,并且还有一种我没想过的更好的方法。

1 个答案:

答案 0 :(得分:5)

我的第一个建议是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并仅在测试期间(使用apply_all_roles)将Simple :: Tax应用于它。

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

 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());

因为handles会将角色中的所有方法委托为新代理,这几乎与自己编写角色相同。