Arquivo da tag: Java

Representação e persistência de valores monetários em Groovy com o padrão Money

Grande parte das aplicações comerciais manipulam valores monetários. Uma venda aqui, um estorno ali, um troco acolá, um parcelamento de um valor não inteiro … Todos esses movimentos devem ser devidamente registrados de forma precisa, pois aparentemente 0.01 R$ pode não fazer diferença para a farmácia da esquina da rua de sua casa, mas imagina isso acontecendo entre as centenas de movimentos de uma distribuidora internacional de medicamentos que cria parcerias e colaboradores por todo o mundo.

Implemente um pequeno sistema de PDV em Java ou qualquer outra linguagem e esteja disposto a encarar as mais inemagináveis lógicas, possibilidades e restrinções …. entre elas:

Um cliente informa os ítens que deseja levar e solicita o fechamento da venda. O funcionário solicita seu nome e procura um bônus de crédito acumulado, para um desconto no movimento. O sistema informa que o cliente tem 7.55 % de desconto acumulado para o próximo movimento. E ele possui um descontinho de 2.45 % por ter mais de 99 anos . Somando aí, temos 10 % de desconto. Bem, somando os ítens do cliente o sistema chega ao valor de 170 R$ e o sistema solicita ao cliente a forma de pagamento, que escolhe em 7 vezes. Fazendo umas continhas tirando o desconto do valor final fica em 153 R$ … dividindo isso em 7 parcelas nós temos 7x de 21,857142857142857142857142857143 … R$

O valor dessa parcela é válida ?? Existe essa quantia monetária em Reais ?? Como fazer: aproximar para 21.85 R$, (sendo que 7 parcelas 21.85 R$ totalizam   152,95 R$ e o caixa da loja perderá 5 cents ) ???

Eu tambem me deparei com essa situação e resolvi escutar a experiência da comunidade, encontrando o padrão Money. Antes vale destacar os livros e sites que fundamentaram essa implementação:

Assim como minhas necessidades quanto à implementação:

  • Preciso de operações de soma e subtração entre valores monetários
  • Preciso de operações de multiplicação e divisão entre valores monetários e escalares ( você não multiplica 10 R$ por 20 R$, multiplica ?)
  • Preciso persistir esse valor
  • Por enquanto, só trabalho com moeda brasileira, sem conversões e internacionalização

E para não ser um artigo inútil a você – caro leitor, e para a WEB, apresento-lhes um comentário sucinto da seguinte implementação da classe Money em groovy:

package br.netsoft

import org.hibernate.*;
import org.hibernate.usertype.*;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.text.DecimalFormat;
import java.util.List;
import java.util.Locale;
import java.util.Properties;

import org.hibernate.usertype.UserType;
import java.sql.Types;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.*;
import java.io.Serializable;

/**
 *
 *  Implementação do padrão Money. Um Value Object
 *  Esta classe deve ser usada p/ implementar todos os valores monetários do sistema
 *  
 *  @author Lucas Simao
 *  @since 28/09/09
 *
 */
class Money implements UserType,Comparable<Money>,Serializable{

    private final static int CENT_FATOR = 100
    private static final Locale BR_LOCALE = new Locale('pt','BR');
    private static final DecimalFormat moneyFormat = new DecimalFormat('¤ ###,###,##0.00',new DecimalFormatSymbols(BR_LOCALE));
    long quantia // em centavos
    private Class targetClass;
    public final static Money ZERO = Money.newMoneyFromCents(0)



    /**
     *   Cria um novo valor monetário, à partir da quantia informada em <b> Centavos (R$) </b>.
     *   Exemplo:
     *
     *   <code>
     *      def m1 = Money.newMoneyFromCents(100) cria 1 real
     *   </code>
     *
     *   @since 28/09/09
     *   @param cents  O valor monetario, em centavos.
     *   @author Lucas Simao
     *
    */
    public static Money newMoneyFromCents(long cents){
        return new Money(quantia:cents)
        
    }

    /**
     *   Cria um novo valor monetário, à partir da quantia informada em <b> Reais (R$) </b>.
     *   Exemplo:
     *
     *   <code>
     *      def m1 = Money.newMoneyFromReal(100) // cria 100 Reais
     *      def m2 = Money.newMoneyFromCents(100) cria 1 real
     *   </code>
     *
     *   @since 28/09/09
     *   @param quantiaEmReais  O valor monetario, em reais.
     *   @author Lucas Simao
     *
    */
    public static Money newMoneyFromReal(double quantiaEmReais) {
        return new Money(quantia:quantiaEmReais * CENT_FATOR)
    }


    def getValorEmReais(){
        return this.quantia/100.0;
    }

    def getValorEmCentavos(){
        return this.quantia;
    }

    def plus(Money other){
        return adicionar(other)
    }

    public int compareTo(Object o){
        Money other = (Money)o
        return (this.quantia - other.quantia)
    }

    def minus(Money other){
        return subtrair(other)
    }

    def multiply(Double value){
        def valor_em_reais = this.quantia/100.0
        return Money.newMoneyFromReal(valor_em_reais*value)
    }

    public Money adicionar(Money qtde){
        return new Money(quantia: (qtde.quantia+this.quantia))

    }
    
    public Money subtrair(Money qtde){
        if (this.quantia<qtde.quantia){
            throw new IllegalStateException('Quantidade a ser subtraída maior que a quantia')
        }
        else{
            return new Money(quantia: (this.quantia-qtde.quantia))
        }}

  

    public List<Money> alocar(int n){
        
        long value = this.quantia/n
        Money lowResult = newMoneyFromCents(value);
        Money highResult = newMoneyFromCents(lowResult.quantia + 1);
        def results = [] as List
        int remainder = this.quantia % n;
        for (int i = 0; i < remainder; i++) results<< highResult;
        for (int i = remainder; i < n; i++) results << lowResult;
        return results;

    }

    public boolean equals(Money other){
        return (other.quantia == this.quantia)
    }

    public String toString(){
        return moneyFormat.format(this.getValorEmReais())
    }


    


    public boolean isMutable() { return false; }

    public Object deepCopy(Object value) { return value; }

    public Serializable disassemble(Object value){ return (Serializable) value; }
    
    public Object assemble(Serializable cached, Object owner) { return cached; }

    public Object replace(Object original, Object target, Object owner) { return original; }

    public boolean equals(Object x, Object y) { if (x == y) return true; if (x == null || y == null) return false; return x.equals(y); }


    public int hashCode(Object x) { return x.hashCode(); }

    // Cria um objeto Money à partir da quantia em centavos armazenada no BD
    public Object nullSafeGet(ResultSet resultSet, String[] names, Object owner) throws SQLException {
        Long value = resultSet.getLong(names[0]);
        if (value){
            return Money.newMoneyFromCents(value)
        }
        else{
            return Money.newMoneyFromCents(0L)
        }
    }

    // Salva o objeto Money representado pelo parâmetro value
    // como o valor em centavos
    public void nullSafeSet(PreparedStatement statement, Object value, int index) throws HibernateException, SQLException
    {
        if (value == null)
        {
            statement.setNull(index, Hibernate.LONG.sqlType());
        }
        else
        {
            statement.setLong(index, value.getValorEmCentavos());
        }
    }

        public void setParameterValues(Properties parameters) {
        String targetClassName = parameters.getProperty("targetClass");
        try { targetClass = Class.forName(targetClassName); }
        catch (ClassNotFoundException e)
        { throw new HibernateException("Class " + targetClassName + " not found ", e); } }

    public Class returnedClass() { return targetClass; }
    def SQL_TYPES = [Hibernate.LONG.sqlType()];

    public int[] sqlTypes() { return SQL_TYPES; } 
}

Sobre a classe Money:

  • Internamente, a quantia monetária é armazenada em centavos (propriedade quantia)
  • A classe implementa org.hibernate.usertype.UserType para definir um tipo de dados personalizado, no caso valores monetários. Os métodos  assemble, deepCopy, desassemble, equals, hashCode, isMutable, nullSafeGet, nullSafeSet, replace, returnedClass, sqlTypes são todos especificados por aquela interface e por isso implementados pela classe Money
  • Esta classe assume valores em reais. Por isso a ausência de alguma menção ao tipo de moeda
  • Ela é baseada no padrão Quantity, que encapsula e associa uma quantia com alguma unidade. Este por sua vez (quantity) deriva do padão Value Object
  • Existem dois métodos que dão vida a esta classe: newMoneyFromCents e newMoneyFromReal
  • Funcionalidades a serem implementadas: Internacionalização e conversão
  • Como groovy é uma linguagem dinâmica e permite sobrecarga de operadores, os métodos plus, minus, multiply e compareTo permite que você manipule facilmente instâncias de Money tornando válido o seguinte código:
def m1 = Money.newMoneyFromCents(100)
def m2 = Money.newMoneyFromReal(10.50)
def m3 = m1 + m2
println m3.toString() // imprime R$ 11.50
println m3 * 2 // imprime R$ 23.00

// resolvendo o problema descrito no início deste post

def m4 = Money.newMoneyFromReal(153.0)

// O método alocar irá retornar um array de tamnho 7, contendo 5 parcelas de
// R$ 21.86 e 2 de R$ 21.85, totalizando R$ 153.00

m4.alocar(7).each{ parcela -> println parcela }

Resumo da ópera: Implementado o padrão Money em Groovy, ao mesmo tempo, definindo um novo tipo de dados através da API org.hibernate.usertype do Hibernate.

Utilizando a classe Money com o Framework Grails

Simplesmente, redefina sua propriedade que armazena valores monetários ( que possivelmente esteja usando a classe Double ou BigDecimal ) para  o  tipo Money, além disso, informe ao Grails através da DSL de mapeamento que o tipo de dado Money é definido através das API’s do Hibernate, assim:

class Produto {
Money valorVenda
 static mapping = {
columns{
valorVenda type:Money
}
}
}

Patterns of Enterprise Application Architecture

Anúncios

3 Comentários

Arquivado em Grails, Groovy, Hibernate

Experiências com Maven e Ant+Ivy …

Decididamente, fico com a combinação Ant+Ivy. Antes deixe-me expor o porquê  da escolha. Andei por umas semanas pesquisando e lendo em blogs opiniões de desenvolvedores experientes sobre essas ferramentas, e cheguei à uma conclusão: Eu mesmo terei que sentir a utilidade das duas no meu dia-a-dia. Bom, eu ainda não tinha usado o Maven à sério e fui atrás do livro Better Builds with Maven . Um tempo atrás eu li também um sobre Ant e, depois de empregrar ambos em projetos de porte e importância iguais e diferentes, formei uma opinião sobre isso.

Maven

Ao meu ver, Maven enxugou o Ant e acrescentou o que a comunidade sentia falta no Ant, o gerenciamento de todo o processo de desenvolvimento e não apenas o build. Eu sou adepto do Ant, mas assumo que há muito Ctrl+C e Ctrl+V quando se está escrevendo um build p/ projetos diferentes. Ou seja, todo projeto possui etapas semelhantes que levam deste a preparação do ambiente de desenvolvimento à implantação do sistema (deploy) seja em ambiente de produção ou testes. Além disso, o HD da máquina de um desenvolvedor Ant (sem Ivy) vai embora “rapidin”, pq vc acaba pegando o hábito de criar uma pasta “lib” na raiz do seu projeto ou sub-projetos e isso é redudante e torna-se estressante. O Maven gerencia todo o ciclo de desenvolvimento além das depenências do seu projeto, centralizando em um repositório todas as dependências de todos os seus projetos, isso é muito bacana. Bom, o que não me agradou muito foi a integração com o eclipse. EU não me agradei do plugin, achei-o muito simples. E outra coisa que não me agradou muito foi o POM . Eu já odeio XML, e p/ piorar a legibilidade do POM descrece à medida que seu projeto  cresce … Em projetos simples até que vai, mas vai inventar de usar ele p/ gerenciar um multi-projeto com sub-projetos que possuem as mesmas dependêcias, só que com versões diferentes e coloca tudo isso em um servidor de integração contínua …. Tudo bem que se vc for comparar com o equivalente em Ant, o Maven dá de pau quanto ao tamanho do arquivo, mas cabe aqui a frase de Einstein : ” Tudo deveria se tornar o mais simples possível, mas não simplificado”.

Ant+Ivy

Essa é a minha escolha. Com Ant eu esqueço o maldito paradigma declarativo e tenho a sensação de estar programando sobre uma plataforma imperativa. Você tem controle sobre todo o processo (embora é importante frisar que a intenção do Ant é o build, não o gerenciamento do seu projeto ) e se tiver experiência e for organizado seus scripts serão grandes completos, porém legíveis. O que faltava era um gerenciador de dependências, e o Ivy então ocupa essa lacuna. O ant integra-se com eclipse de forma completa e inteligente, embora o IvyDE (plugin do Ivy p/ o Eclipse) ainda tenha lá seus bugs …

Uma dica ….

Forme opinião, cada um tem seu modo de ver uma ferramenta, uma linguagem, um SO, mulheres, carros e a vida …. As ferramentas que citei tem vasta documentação da Web e a comunidade também é enorme, ambas em torno de cada ferramenta. E vc, Apache+Ivy ou Maven ????

1 comentário

Arquivado em Ant, Java, Maven

Acelerando o desenvolvimento de GUI’s

Um dia desses, estava refletindo sobre o processo de desenvolvimento de aplicações comerciais desktop. Fiz um retrospecto da minha evolução em um projeto e notei que estava gastando demasiadamente meu precioso tempo me precupando  com coisas chatas e repetitivas como o desenvolvimento de GUI. É muito chato se comportar como um “macaco digitador de código”.

Toda essa carga de precaução em cada código que crio para essa aplicação vem se acentuando desde que comecei a babar pelo Grails e pela plataforma WEB. Definitivamente, quando tiver a primeira oportunidade mudo a plataforma dessa aplicação p/ a WEB sem pena e nem dó, sorte minha que o core da lógica de negócio está bem separada da UI.

Enfim, voltemos ao Desktop. Aquela papagiada de CRUD tava me dando nos nervos … Eram JTextFields para cá, JComboBox para lá, os malditos JTables e seus TableModels … enfim Swing. Nuss …

Muito código repetido, requisitos transversais à lógica de negócio, setters p/ cá, getters p/ lá … aí estão os bedsmells em meu código. Na minha mente logo vem o Martin Fowler gritando, exigindo e experniando: REFRACTORING !!!!!!! Calma Fowler, todo esse código que estou fazendo no braço pode ser gerado automaticamente. Vejamos, cada tela representa um POJO que deve ser carregado através do mecanismo de persistência. Eu preciso encapsular esse objeto de forma a acessar cada um de seus atributos, assim como conhecer de ante-mão os próprios. Estudei a geração de código-fonte com templates através do FreeMarker .

Blz, montei toda a estrutura baseado no modelo MVC do framework CoCoa, tava indo muito bem e tava ganhando caras de um “Lucas MVC Style Framework”. Depois de um tempo, um incômodo: Por que telas de cadastro tão ridículas, com uma mísera quantidade de campos requer tanto código-fonte gerado ?? Comecei a pensar sobre o impacto na performance à longo prazo e na facilidade de manutenção. É … meu “Lucas MVC Style Framework”  tinha tomado o caminho errado …

Então fui em busca de soluções e lembrei-me de um tal de Gênesis, um framework brazuca que ouvi falar tempo atrás mas ñ havia dado confiança. O gênesis trazia tudo que estava precisando de forma clean:

  • Swing binding
  • Formatadores, conversores
  • Remoting
  • Suporte a transações
  • Paginação

O uso é simples, o framework é bem documentado, há uma lista de discussão p/ usuários, já tem alguns componentes out of the box “à lá” plug-and-play style e funcionalidades como o binding de componentes e atributos de beans que economizaram-me linhas de código e tempo p/respirar aliviado. Bendito sejão os @Forms e @ViewHandlers. Vale a pena conferir.

Deixe um comentário

Arquivado em desktop, freemarker, genesis, mvc