2012-01-25 1 views
4

나는 두 가지 역할을한다고 말한다 : Simple :: Tax와 Real :: Tax. 테스트 환경에서는 Simple :: Tax를 사용하고 제작시 Real :: Tax를 사용하려고합니다. 이 작업을 수행하는 가장 좋은 방법은 무엇입니까?Moose에서 모의 ​​역할을 처리하는 방법은 무엇입니까?

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

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

내 두 번째 생각은 패키지의 몸에 문이 다른 with 문을 사용하는 경우를 사용했다 : 내 첫번째 생각은 다른 역할을 가진 개체를 만들 수 new 방법의 다른 버전을 사용하는 것이 었습니다

이 중 하나가 다른 것보다 좋으며, 둘 중 하나에 대해 끔찍한 점이 있으며 아직 생각하지 못한 더 나은 방법이 있습니다.

답변

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 구현을 재정의하기 위해 여러 가지 작업을 수행 할 수 있습니다.

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

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

그럼 항상 위로 구성 Complex::Tax을 가지고 ( apply_all_roles 사용) 만 테스트 중에 간단한 :: 세금을 적용

예를 들어, 당신은 너무 같은 간단한 :: 세금을 정의 할 수 있습니다.

그러나 생산에서 (단순히 테스트가 아닌) 단순 :: 세금 및 복합 :: 세금이 필요한 경우 가장 좋은 방법은 구성 관계 (않는 경우)에서 위임 관계 (가진 경우) 리팩터링입니다. 당신이 그것을 무시하려는 경우

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 때문에이이 구성하는 데에 실질적으로 동일하다 새로운 프록시로 역할에서 모든 방법을 위임합니다 너 자신의 역할.

+0

그래서 간단한 세금 역할의'around calculate_tax' 메소드는 실제로 객체에 대해'calculate_tax' 메소드를 호출하지 않습니다. 맞습니까? 나는 이것이 가장 깨끗한 방법이라고 생각한다. –

+0

정확하게, 'around'를 사용하여 부모 메소드를 완전히 대체합니다. – perigrin

관련 문제