Let's see if the predictions of the SO request robot come true, explicitly released based only on the name of the question:
The question you ask seems subjective and is likely to be closed.
Using Perl / Moose, I would like to bring inconsistency between the two methods presented by trading articles. Let the article have a name , quantity and price . The first way this seems to be is the quantity given for any numerical value, including decimal values, so you can have 3.5 meters of rope or cable. The second one I have to interact with is, alas, inflexible, and requires quantity be an integer. So I have to rewrite my object in order to set quantity to 1 and include the actual value in name . (Yes, itโs a hack, but I wanted this example to be simple.)
So the story here is that one property value affects the other property values.
Here's the working code:
#!perl package Article; use Moose; has name => is => 'rw', isa => 'Str', required => 1; has quantity => is => 'rw', isa => 'Num', required => 1; has price => is => 'rw', isa => 'Num', required => 1; around BUILDARGS => sub { my $orig = shift; my $class = shift; my %args = @_ == 1 ? %{$_[0]} : @_; my $q = $args{quantity}; if ( $q != int $q ) { $args{name} .= " ($q)"; $args{price} *= $q; $args{quantity} = 1; } return $class->$orig( %args ); }; sub itemprice { $_[0]->quantity * $_[0]->price } sub as_string { return sprintf '%2u * %-40s (%7.2f) %8.2f', map $_[0]->$_, qw/quantity name price itemprice/; } package main; use Test::More; my $table = Article->new({ name => 'Table', quantity => 1, price => 199 }); is $table->itemprice, 199, $table->as_string; my $chairs = Article->new( name => 'Chair', quantity => 4, price => 45.50 ); is $chairs->itemprice, 182, $chairs->as_string; my $rope = Article->new( name => 'Rope', quantity => 3.5, price => 2.80 ); is $rope->itemprice, 9.80, $rope->as_string; is $rope->quantity, 1, 'quantity set to 1'; is $rope->name, 'Rope (3.5)', 'name includes original quantity'; done_testing;
I wonder, however, if there is a more perfect idiom for this in the Muse. But maybe my question is all subjective and deserves a quick close. :-)
UPDATE based on perigrin answer
I adapted the perigrin code example (minor bugs and syntax 5.10) and tagged my tests at its end:
package Article::Interface; use Moose::Role; requires qw(name quantity price); sub itemprice { $_[0]->quantity * $_[0]->price } sub as_string { return sprintf '%2u * %-40s (%7.2f) %8.2f', map $_[0]->$_, qw/quantity name price itemprice/; } package Article::Types; use Moose::Util::TypeConstraints; class_type 'Article::Internal'; class_type 'Article::External'; coerce 'Article::External' => from 'Article::Internal' => via { Article::External->new( name => sprintf( '%s (%s)', $_->name, $_->quantity ), quantity => 1, price => $_->quantity * $_->price ); }; package Article::Internal; use Moose; use Moose::Util::TypeConstraints; has name => isa => 'Str', is => 'rw', required => 1; has quantity => isa => 'Num', is => 'rw', required => 1; has price => isa => 'Num', is => 'rw', required => 1; my $constraint = find_type_constraint('Article::External'); =useless for this case
I agree that this provides a better separation of concerns. On the other hand, I'm not sure if this is the best solution for my purpose, as it adds complexity and does not provide for automatic conversion (for which I will have to add more code).