My first suggestion would be something like MooseX::Traits , and then specify different roles when creating the object:
my $test = A->with_traits('Simple::Tax')->new(...); my $prod = A->with_traits('Complex::Tax')->new(...);
But this opens the door to creating A without a role. Therefore, thinking about this further, I think you have an X / Y problem. If Simple::Tax used only for modeling Complex::Tax in a test environment, you can do several things to override the implementation of Complex :: Tax.
For example, you can simply define Simple :: Tax as follows:
package Simple::Tax; use Moose::Role; requires 'calculate_tax'; around calculate_tax => sub { int($_[1]->price * 0.05) };
Then there is always A compose Complex::Tax and apply Simple :: Tax to it only during the tests (using apply_all_roles ).
If you need Simple :: Tax and Complex :: Tax as in production (and not just for testing), your best choice is refactoring from the relationship of the composition (is) to the relationship with the delegation (is).
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());
Since handles delegates all methods from the role as new proxies, this is almost identical to composing the role on its own.