Enigma de Matt Foemmel
Suponha que eu tenha que alocar 5 centavos entre duas contas: 70% para uma e 30% para outra. O cálculo matemático resulta em 3,5 centavos e 1,5. Não importa como será arredondado os valores, ou tu ganharás 1 centavo ou perderás 1 centavo.Como funciona
A idéia básica é ter uma classe Dinheiro com campos para o valor e a moeda corrente. Você pode armazenar a quantidade como um tipo inteiro ou tipo decimal fixo. O tipo decimal é mais fácil para algumas manipulações, o integral para outras. Deve-se evitar completamente qualquer tipo de ponto flutuante, pois isso introduzirá o tipo de problemas de arredondamento cuja finalidade de Dinheiro é evitar.A solução favorita de Martin Fowler foi essa: ter uma função de alocação na classe Dinheiro. O parâmetro para o alocador é uma lista de números, representando a proporção a ser alocada. O alocador retorna uma lista de objetos dinheiro, garantindo que nenhum centavo seja perdido espalhando-os pelos objetos dinheiro alocados de um modo que, de fora, pareça pseudo-randômico.
Com os números em ponto flutuante descartados, a escolha fica entre inteiros e decimais de ponto fixo, o que em Java se resume a BigDecimal, BigInteger e Long.
class Dinheiro{
private long quantia;
private Currency moeda;
}
Usar o long nos permite ter expressões matemáticas mais legíveis. Porém se o número ficar muito grande, ocorrerá um erro de estouro (overflow). A conclusão que se chega é que é necessário a criação de vários construtores para cada tipo numérico:
class Dinheiro{
public Dinheiro(long quantia, Currency moeda){
this.moeda = moeda;
this.quantia = quantia * fatorCentavos( );
}
public static final int[ ] centavos = new int[ ] {1,10,100,1000};
private int fatorCentavos( ){
return centavos[moeda.getDefaultFractionDigits( )]
}
}
Diferentes moedas tem diferentes quantias fracionárias. A classe Currency lhe informará o numero de dígitos fracionários em uma classe. O Dinheiro é um Objeto Valor*, então deve ter suas operações de igualdade e código hash sobrescritas para serem baseadas na moeda corrente e na quantia. Se você quiser alocar uma importância em dinheiro para muitos destinatários e não quiser perder centavos, irá precisar de um método de alocação.
class Dinheiro{
public Dinheiro[ ] alocar (long[ ] razoes){
long total = 0;
for(int i = 0; i ;razoes; i++) total += razoes;
long resto = quantia;
Dinheiro[ ] resultados = new Dinheiro[razoes.length];
for(int i = 0; i; resultados.length; i++){
resultados[ i ] = novoDinheiro(quantia * razoes[ i ] / total);
resto -= resultados[ i ].quantia;
}
for(int i = 0; i; resto; i++)resultados[ i ].quantia++;
return resultados;
}
}
Pode-se utilizar isso para resolver o Enigma de Foemmel
class Dinheiro{
public void testeAlocar( ){
long[ ] alocacao = {3,7};
dinheiro[ ]resultado = dinheiro.dolares(0.05).alocar(alocacao);
equals(Dinheiro.dolares(0.02), resultado[0]);
equals(Dinheiro.dolares(0.03), resultado[1]);
}
}
Com isso, uma classe Dinheiro pode ser utilizada para quase todo o cálculo numérico em ambientes OO. A razão principal é encapsular a manipulação do comportamento de arredondamento, o que ajuda a reduzir os problemas de erros de arredondamento. Outra razão para uso de Dinheiro é tornar muito mais fácil o trabalho com diversas moedas. A objeção mais comum a Dinheiro é o desempenho, raramente tenha-se ouvido falar de casos em que ele fez qualquer diferença perceptível, e mesmo nesses casos o encapsulamento, muitas vezes, torna mais fácil a otimização.*Objeto Valor(Value Object):Um objeto pequeno e simples, como dinheiro ou um intervalo de datas, cuja igualdade não é baseada em identidade.
Referências: Padrões de Arquitetura de Aplicações Corporativas - Martin Fowler - páginas 455 a 457.
Nenhum comentário:
Postar um comentário