MoneyBag

Money Bag

O padrão Money Bag (Bolsa de dinheiro) é uma extensão do padrão Money (Dinheiro). O padrão Money é principalmente útil quando trabalhamos com uma única moeda ou multiplas moedas mas elas não podem interagir directamente. Por exemplo, não podemos somar euros com dollars. Contudo em certas circunstâncias é mais simples permitir que haja essa soma porque o resultado final será convertido para uma outra moeda.

O problema

A aplicação trabalha intensamente com valores monetários e é necessário operar sobre valores expressos em moedas diferentes. Usar o padrão Money ajuda, mas obriga a converter tudo para uma certa moeda base antes de proseguir para que os valores sejam coerentes. Se for somado um valor em euro a um valor em dolar e depois subtraido o fator de conversão afeta o resultado final quando na realidade o valor de euro subtraido seria subtraido do valor em euro presente no calculo.

A solução

A solução passa por usar um objeto do tipo Money que possa comportar vários valores em moedas diferentes. Para usar Money Bag é necessário que se use o padrão Money para começar já que todos Money Bag é também um Money.

Como funciona

Criamos uma classe do tipo MoneyBag e fazemos uma implementação equivalente à de Money. A diferença é que nas operações aritméticas deixaremos que a operação de soma e subtração aconteça mesmo quandos os objetos não têm a mesma moeda.

Teremos duas classes: Money que guarda apenas um valor e uma moeda e MoneyBag que guarda vários valores e várias moedas.

Implementação

A impletação é relativamente simples. Fazemos MoneyBag herdar Money e alteramos as operações aritméticas de soma e subtração. Teremos que adicionar um método simplify() para que possamos reduzir um MoneyBag a um Money quando isso for possivel (i.e. quando MoneyBag contiver o valor de apenas uma moeda).

01
02
03 public class Money {
04
05 int amount;
06 Currency currency;
07
08 public Money plus ( Money other ){
09 // usamos o mecanismo de moneyBag porque é generico. Isso evita usar ifs
10 return new MoneyBag () .plus ( this ) .plus ( other ) .simplify () ;
11 }
12
13 public Money subtract ( Money other ){
14 return new MoneyBag () .plus ( this ) .subtract ( other ) .simplify () ;
15 }
16
17 public Money negate (){
18 return new Money ( -amount, currency ) ;
19 }
20 // nivel de pacote
21 int getAmount ( Currency c ){
22 if ( c.equals ( this .currency )){
23 return amount;
24 }
25 return 0 ;
26 }
27
28 public Money simplify (){
29 return this ;
30 }
31 }

Código 1:

01
02
03 public class MoneyBag extends Money {
04
05 private Map bag = new HashMap;
06
07 public Money plus ( Money other ){
08 int bagAmount = this .getAmount ( other.getCurrency ()) ;
09 int otherAmount = other.getAmount ( other.getCurrency ()) ;
10 int total = bagamount + otherAmount;
11 if ( total== 0 ){
12 bag.remove ( other.getCurrency ()) ;
13 } else {
14 bag.put ( other.getCurrency () , new Integer ( total )) ;
15 }
16 }
17
18 public Money subtract ( Money other ){
19 return this .plus ( other.negate ()) ;
20 }
21
22 int getAmount ( Currency c ){
23 Integer amount = bag.get ( other.getCurrency ()) ;
24 if ( amount== null ){
25 return 0 ;
26 }
27 return amount.intValue () ;
28
29 }
30
31 public Money simplify (){
32 if ( bag.size () == 1 ){
33 Map.Entry entry = ( Map.Entry ) bag.entrySet () .iterator.next () ;
34 return new Money ( entry.getValue () , entry.getKey ()) ;
35 } else {
36 return this ;
37 }
38 }
39 }

Código 2:

Esta é uma das muitas implementações possiveis. O ponto importante é que MoneyBar contenha um conjunto de pares moeda-valor. Poderiamos ter usado a propria classe Money para manter esses valores mas optei por usar um mapa de moeda e inteiros.(Porquê inteiros ?)

Conversões

Se estamos trabalhando com várias moedas provávelmente estamos trabalhando também com conversões. Se existir um serviço de conversão disponivel podemos implementar um método simplify() que força que as conversões aconteçam e seja retornado um único valor numa única moeda.

01
02
03 public Money simplify ( Date date, ConvertionService converter, Currency currency ){
04
05 Money res = Money.zero ( currency ) ;
06 for ( Map.Entry entry : bag ){
07 Money money = new Money ( entry.getValue () , entry.getKey ()) ;
08 res = res.plus ( converter.convert ( date, money, currency )) ;
09 }
10 return res;
11
12 }

Código 3:

Padrões relacionados

Em termos práticos fazer MoneyBag herdar directamente de Money pode ser um problema. Por exemplo, o objeto ConvertionService recebe como argumento um Money mas é esperado que seja um valor uma única moeda. Neste caso é previdente usar o padrão Composite (pelo menos a ideia por detrás do padrão Composite) Desta forma podemos modelar uma interface comum aos dois tipos de objeto e construir implementações independentes. A interface é necessária para o retorno de plus e outros métodos aritméticos.

01
02
03 public interface MoneyValue {
04
05 public MoneyValue plus ( MoneyValue other ) ;
06 public MoneyValue subtract ( MoneyValue other ) ;
07 public MoneyValue simplify () ;
08 public Money simplify ( Date date, ConvertionService converter, Currency currency ) ;
09 }
10
11 public class Money implements MoneyValue {}
12
13 public class MoneyBag implements MoneyValue () ;

Código 4:

No segundo método simplify() o retorno é concerteza de um só tipo de moeda. Por isso o retorno é garantidamente do tipo Money. Algumas operações de Money como distribute() podem ser mais complicadas de implementar para MoneyBag. Como temos classes separadas para Money e MoneyBag isso não é um problema.

Licença

Creative Commons License Sérgio Taborda
Este trabalho é licenciado sob a
Licença Creative Commons Atribuição-Uso Não-Comercial-Não a obras derivadas 3.0 Genérica .

Deixe um comentário