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