Marcelo Burgos Morgade Cortizo Analista de Sistemas Pleno da PETROBRAS S.A., Msc. em Redes de Computadores e entusiasta do Java h quase 10 anos. De que se trata o artigo: Uso da biblioteca Beans Binding para implementao do padro Presentation Model em interfaces Swing para aplicaes Java e como o uso desta tcnica permite construir interfaces desktop para sistemas Java de forma simples e produtiva. Para que serve: Fornecer um meio para implementar clientes desktop ricos capazes de intercambiar dados automaticamente entre a interface e o modelo de negcio. Permite tambm controlar o comportamento de uma tela apenas trabalhando com um modelo de apresentao simples e reutilizvel, sem a necessidade de manipulao direta de componentes grficos. Em que situao o tema til: Ao analisar as alternativas de interfaces para clientes ricos de sistemas corporativos, pois importante considerar os benefcios da utilizao de interfaces Swing e as tecnologias que facilitam o desenvolvimento e implantao como o Beans Binding e o Java Webstart. Swing + Beans Binding: O binding (ou vinculao) de propriedades de objetos simples a propriedades de componentes grficos do Swing permite construir interfaces que trocam dados automaticamente entre a interface grfica e as entidades de um sistema. Tambm permite implementar de forma simples o padro Presentation Model, atravs do qual possvel controlar o comportamento das telas apenas trabalhando com um modelo de apresentao simples e reutilizvel, sem a necessidade de manipulao direta de componentes grficos. Este artigo apresenta a API de Beans Binding e como seu uso integrado com o Swing GUI Builder do NetBeans 6.5 (anteriormente conhecido como Matisse). Veremos como a API de binding consegue sincronizar as propriedades de diferentes objetos e particularmente de componentes grficos do Swing. Finalmente, ser explicado como esta API pode ser usada como ferramenta facilitadora para implementao do padro de projeto chamado de Presentation Model. Java no desktop? Esta pergunta talvez ainda seja feita pelos desenvolvedores mais experientes. Na realidade, quem conviveu com os primeiros anos do Java tem todo o direito de questionar a viabilidade deste tipo de aplicao. O Swing trouxe componentes grficos elegantes baseados num modelo MVC, mas no to simples de trabalhar. Os gerenciadores de layouts, apesar de eficientes, no tinham a agilidade de ambientes WYSIWYG para prototipao rpida de telas. Porm, com o tempo a situao mudou bastante. A plataforma e as IDEs evoluram. Hoje, com as novas ferramentas e com o Java Webstart para distribuio automatizada, uma interface desktop para aplicaes corporativas uma alternativa s interfaces Web no apenas vivel, mas tambm muito produtiva e eficiente. O recente lanamento JavaFX o ltimo avano nesta escalada, mas ainda uma tecnologia em processo de amadurecimento que aos poucos vai ganhar suporte mais completo das IDEs. Uma das features interessantes da JavaFX foi o suporte nativo ao binding de propriedades (ou vinculao, em traduo livre). Porm, fora da JavaFX tambm possvel implementar a vinculao de propriedades entre dois objetos, e esta uma das ferramentas mais interessantes para acelerar o desenvolvimento de aplicaes desktop. Neste artigo vamos demonstrar como a biblioteca BeansBinding implementa esse processo e como utiliz-lo de forma produtiva atravs do suporte oferecido pelo NetBeans 6.5. Beans Binding Manter duas propriedades de dois objetos em sincronia mais complicado do que parece, principalmente se isso deve ser encapsulado em uma API com baixo nvel de intruso no cdigo de negcio. De forma rpida e crua, possvel vincular a propriedade de dois objetos como na forma usada na Listagem 1. Aps este trecho de cdigo, o texto Z impresso. Enquanto o binding estiver ativo, qualquer alterao na propriedade nome do objeto c1 automaticamente refletida em c2 (e vice-versa). A API Beans Binding baseada nas especificaes de propriedades JavaBeans e na capacidade de instalar escutadores de estados nos objetos a serem sincronizados. Listagem 1. Listagem 1. Uso manual do BeansBinding Contato c1 = new Contato(); Contato c2 = new Contato(); Binding binding = Bindings.bind(UpdateStrategy.READ_WRITE, c1, BeanPropery.create("nome"), c2, BeanProperty.create("nome")); Binding.bind(); c1.setNome("Z"); System.out.println(c2.getNome()); Porm, esta automao exige que a classe Contato respeite o contrato de definio de propriedades e publicao de eventos da especificao JavaBeans. De forma mais objetiva, o Beans Binding exige que a classe implemente os padres setters e getters de nomeao para encapsular propriedades, e a disponibilizao dos mtodos addPropertyChangeListener() e removePropertyChangeListener(). Essas exigncias podem ser observadas na Listagem 2. O principal a se notar neste cdigo o uso do PropertyChangeSupport, que uma classe utilitria do pacote JavaBeans responsvel por gerenciar e notificar os escutadores de mudana de propriedade (implementadores da interface PropertyChangeListener). A classe Contrato deve ter a responsabilidade extra de notificar todas as mudanas de valores de suas propriedades para que o Beans Binding saiba os momentos em que a sincronizao necessria (usando o mtodo firePropertyChange() do propertyChangeSupport). Alguns podem achar que h bastante cdigo intruso nesta classe, mas na verdade ela est apenas implementando uma das primeiras e mais recomendas especificaes do Java (ver quadro POJO: Ser ou no ser... Eis a questo). Listagem 2. Classe Contato package org.morgade.exemplo.entity;
public boolean isPessoal() { return empresa == null; }
public void addPropertyChangeListener (PropertyChangeListener listener) { propertyChangeSupport. addPropertyChangeListener(listener); }
public void removePropertyChangeListener (PropertyChangeListener listener) { propertyChangeSupport. removePropertyChangeListener(listener); }
} Assim, na Listagem 1, a chamada Bindings.createAutoBinding() registra PropertyChangeListeners nos objetos c1 e c2, para que cada mudana detectada na propriedade nome seja notificada para posterior sincronizao dos valores nos dois objetos. Cada binding tem uma origem (o objeto c1) e um destino (o objeto c2). A enum UpdateStrategy, como sugere o nome, define como se aplica a sincronizao: UpdateStrategy.READ_WRITE: Mudanas na propriedade do objeto fonte so aplicadas na propriedade do objeto destino e vice-versa; UpdateStrategy.READ: Mudanas na propriedade do objeto fonte so aplicadas na propriedade no objeto destino, mas mudanas na propriedade do objeto destino no so aplicadas no objeto origem; UpdateStrategy.READ_ONCE: O estado da propriedade do objeto origem aplicado na propriedade do objeto destino apenas no momento da chamada do mtodo bind(). Novamente na Listagem 1, observe como a definio da propriedade a ser vinculada especificada no binding (3 e 5 parmetros). O Beans Binding define a classe abstrata Property para unificar a forma de acesso a propriedades. As seguintes implementaes desta classe so usadas: BeanProperty: Permite definir as propriedades atravs de uma String que suporta vrios nveis de caminhos. Seria possvel, por exemplo, especificar a propriedade endereco.rua, na classe Contato; ELProperty: Permite definir propriedades usando a especificao de Expression Language do Java. possvel, por exemplo, usar a expresso ${not empty nome} para definir uma propriedade da classe Contato que pode ser vinculada a uma propriedade boolean de outro objeto. Dessa forma, o Beans Binding usa as definies de propriedade para propagar as mudanas entre os objetos, controlando problemas de recursividade e possveis ciclos de vinculao e gerenciando caches de propriedade para otimizar a performance. A questo que como sempre estamos pensando em sistemas WEB, no nos preocupamos em implementar a publicao de mudanas de propriedades (j que na WEB quase sempre as propriedades dos objetos so setadas em seqncia durante alguma fase da resposta requisio). Assim, defina detalhadamente seu conceito de POJO antes de responder a questo deste quadro. POJO: Ser ou no ser... Eis a questo O uso dos padres do PropertyChangeSupport nas classes de negcio nos leva a uma questo controversa: Assim minhas classes no deixaro de ser POJOs ?. O problema da necessidade do PropertyChangeSupport nas classes na verdade a falta de suporte nativo do Java para a publicao de eventos de mudanas de propriedades. Qualquer metodologia de binding s possvel com o uso de escutadores de propriedades para disparar as sincronizaes. O importante observar que as classes de apoio publicao de propriedades usadas no cdigo no pertencem API de binding e sim ao pacote JavaBeans (que um pacote padro no Java para publicao de propriedades, e est em qualquer JRE. O prprio NetBeans tem a opo de gerar setters com PropertyChange independente de usar bindings). No fim das contas, definir o que um POJO quase uma questo filosfica. Ser POJO ou no ser POJO neste caso depende do significado exato que voc d a este termo. O fato que a maioria dos chamados POJOs que vemos por a so JavaBeans (objetos Java simples sem dependncias externas, com construtores sem parmetros e mtodos setters e getters representando propriedades). at possvel esconder o cdigo do PropertyChangeSupport usando AOP ou implementando proxies dinmicos, mas nem sempre a publicao da mudana algo que deve ser automatizado, pois faz parte das regras internas contidas no encapsulamento do comportamento da classe. Swing Binding e o padro Presentation Model O Beans Binding mostra seu real poder ao ser combinado com os componentes do Swing. possvel por exemplo fazer o binding da propriedade nome do nosso objeto Contato propriedade text de um JTextField e fazer com que o nome seja atualizado automaticamente medida que o usurio digita na tela. Na mesma tela possvel tambm vincular a expresso como ${not empty nome} propriedade enabled de um boto, que faz com que o mesmo habilite ou desabilite automaticamente quando o campo de texto est vazio ou preenchido. Podemos vincular o contedo de uma JTable a uma java.util.List e ver a tabela se atualizar automaticamente ao fazer operaes simples de adio e remoo na lista. O Beans Binding disponibiliza um pacote chamado Swing Binding, com adaptadores capazes de implementar propriedades sintticas nos componente grficos que so usados nos bindings (como as propriedades elements e selectedElements do JTable e JComboBox). Toda a complexidade de implementao dos modelos de dados para os componentes fica escondida sob o Swing Binding. Essas facilidades obtidas com o Swing Binding o tornam uma ferramenta excelente para aplicar o padro de projeto chamado Presentation Model nas telas de uma aplicao. De maneira geral, o padro Presentation Model prope uma forma de controlar o estado e o comportamento de uma interface grfica com usurio (GUI) apenas manipulando um modelo de classes de negcio, e no atravs de componentes grficos. Um mecanismo de binding entre componentes e objetos de negcio essencial para o funcionamento do Presentation Model. Na seo a seguir veremos como usar os recursos do Netbeans 6.5 para implementar uma tela Swing usando o padro Presentation Model atravs de Beans Binding. Swing Binding no NetBeans 6.5 Nosso projeto de exemplo ser a clssica agenda de contatos, que usa as classes Contato e Empresa, presentes nas Listagens 2 e 3. A tela foi projetada no NetBeans GUI Builder e pode ser vista na Figura 1.
[abrir imagem em janela] Figura 1. Projeto da tela no NetBeans GUI Builder Listagem 3. Classe Empresa package org.morgade.exemplo.entity;
@Override public String toString() { return razaoSocial; }
public void addPropertyChangeListener (PropertyChangeListener listener) { propertyChangeSupport. addPropertyChangeListener(listener); }
public void removePropertyChangeListener (PropertyChangeListener listener) { propertyChangeSupport. removePropertyChangeListener(listener); } } Para criar um presentation model que represente esta tela precisamos pensar em todos os dados necessrios para controlar o estado dela em forma de classes de negcio, classes utilitrias ou tipos primitivos. Este um exerccio comum ao usar este tipo de abordagem, e permite partir facilmente de uma tela projetada como prottipo para uma tela funcional. Observe o nosso presentation model na Listagem 4. O contedo da tabela representado pela propriedade listaDeContatos. Precisamos tambm representar um elemento de estado importante que o elemento selecionado na tabela. Este papel da propriedade contatoSelecionado. O valor dos campos nome, telefone e empresa devem sempre exibir os dados do contato selecionado na tabela. Assim, o presentation model no precisa de propriedades especficas para estes campos. Finalmente, o JComboBox de empresas deve ser preenchido com uma lista, independente do contato selecionado. Para isso usamos a propriedade listaDeEmpresas no presentation model. Listagem 4. Classe AgendaPresentationModel package org.morgade.exemplo.presentation.model; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.util.ArrayList; import java.util.List; import org.jdesktop.observablecollections. ObservableCollections; import org.morgade.exemplo.entity.Contato; import org.morgade.exemplo.entity.Empresa;
public class AgendaPresentationModel { private PropertyChangeSupport property ChangeSupport = new PropertyChangeSupport(this);
private List listaDeEmpresas; private List listaDeContatos; private Contato contatoSelecionado;
public AgendaPresentationModel() { listaDeContatos = ObservableCollections. observableList(new ArrayList()); listaDeEmpresas = new ArrayList(); listaDeEmpresas.add(null); listaDeEmpresas.add (new Empresa("Empresa A")); listaDeEmpresas.add (new Empresa("Empresa B")); listaDeEmpresas.add (new Empresa("Empresa C")); listaDeEmpresas.add (new Empresa("Empresa D")); }
public void novoContato() { Contato c = new Contato(); c.setNome("Novo contato"); c.setTelefone("222-2222"); listaDeContatos.add(c); }
public void excluirContato() { listaDeContatos.remove (contatoSelecionado); }
public List getListaDeContatos() { return listaDeContatos; }
public Contato getContatoSelecionado() { return contatoSelecionado; }
public List getListaDeEmpresas() { return listaDeEmpresas; }
public void addPropertyChangeListener (PropertyChangeListener listener) { propertyChangeSupport. addPropertyChangeListener(listener); }
public void removePropertyChangeListener (PropertyChangeListener listener) { propertyChangeSupport.removeProperty ChangeListener(listener); } } Agora que temos a tela e o presentation model correspondente, precisamos fazer a cola entre os dois. No nosso exemplo, o presentation model ser uma propriedade de nossa tela (apesar do padro de projeto tambm permitir a associao no sentido inverso). Observe na Listagem 5 que o presentation model declarado no cdigo fonte do JFrame, instanciado antes da inicializao dos componentes, e disponibilizado para acesso atravs de um mtodo getModel(). O presentation model deve ser instanciado antes da inicializao dos componentes pois o estado inicial da interface j deve estar disponvel quando a tela for aberta. Isso tudo permite usar os editores de binding do NetBeans e nos permite configurar a ligao da tela com os objetos de negcio sem precisar escrever nenhuma linha de cdigo. Listagem 5. Parte do cdigo da classe AgendaFrame package org.morgade.exemplo.presentation.gui;
public static void main(String args[]) { java.awt.EventQueue.invokeLater(new Runnable() { public void run() { new AgendaFrame().setVisible(true); } }); } } Editar o binding de um componente no NetBeans semelhante a editar uma propriedade. Ao clicar em um componente, observe que o editor de propriedades tem uma aba chamada Binding (ou Vinculao se o seu NetBeans estiver em portugus). Ao clicar no JTextField de nome, e depois no boto de edio do binding da propriedade text, a tela de edio de binding exibida (Figura 2). No campo Binding Source so listados todos os componentes da tela. Como nosso presentation model uma propriedade da tela, selecionamos o componente raiz (por padro com o nome de Form). Ao selecionar o Binding Source, o campo Binding Expression carregado com a rvore de propriedades do mesmo. Observe na Figura 2 que ao selecionar uma propriedade na rvore, uma expresso automaticamente montada para compor o binding do componente. A rvore somente um assistente, uma vez que a expresso pode ser digitada manualmente. No caso do componente textNome, usamos a expresso ${model.contatoSelecionado.nome} para fazer com que o campo sempre exiba o nome do contato selecionado na tabela.
[abrir imagem em janela] Figura 2. de binding da propriedade text do JTextField que mostra o nome
Na aba Advanced do editor de binding existem outras configuraes importantes como conversores de tipos, validaes e valores padro. Neste artigo no detalharemos estas configuraes, mas importante experiment-las para customizar a tela para modelos mais complexos. O editor de binding mais complexo o do JTable e ele deve ser acessado clicando com o boto direito na tabela e selecionado Table Contents .... A aba Table Model semelhante s demais, bastando selecionar a opo Bound (Figura 3). Usaremos a expresso ${model.listaDeContatos} para definir a propriedade que contm a lista de elementos da tabela. Na aba Columns podemos definir um conjunto de colunas com sub-expresses sobre o contexto de um elemento da lista original e assim definir o seu contedo (Figura 4). Clique no boto Insert e configure expresses para as propriedades nome e telefone. Outra propriedade importante para binding do JTable a selectedElement. Ela pode ser acessada tambm na aba Binding do editor de propriedades padro. Ela deve ter a expresso ${model.contatoSelecionado} para que o model seja atualizado automaticamente sempre que o usurio selecionar um novo elemento na tabela.
[abrir imagem em janela] Figura 3. Table Model do editor de binding da propriedade elements do JTable
[abrir imagem em janela] Figura 4. Aba Columns do editor de binding da propriedade elements do JTable A Tabela 1 faz um resumo da configurao de binding de cada elemento da tela. Observe que o JComboBox tambm precisa de configurao da propriedade elements e da selectedElement. O combo usar o mtodo toString() da classe Empresa para exibir os objetos (no caso do componente JList, o binding aceita uma expresso especfica para exibio de cada objeto da lista).
[abrir imagem em janela] Tabela 1. Configurao de binding de cada elemento da tela Pedras no caminho Nem tudo perfeito na histria do Beans Binding. preciso ter conscincia de que o uso da Expression Language para definio das propriedades no binding trazem um pouco mais de dificuldade em possveis operaes de refactoring automatizadas das propriedades, uma vez que as ferramentas ainda no esto preparadas para fazer substituies automatizadas nas expresses. Porm, este o mesmo tipo de problema existente em queries HQL e configuraes de componentes JSF, e dependem de novas ferramentas de suporte para serem resolvidos. Outro problema difcil de compreender a inatividade da especificao JSR-295. Enquanto o NetBeans j prov um suporte de timo nvel ao Beans Binding (que a implementao de referncia da JSR), o time que comanda a especificao e a implementao est parado h um ano. Mas a comunidade que usa a API no deixa barato e uma nova implementao baseada na especificao comeou sua atividade com o sugestivo nome de Better Beans Binding (ver Links). Outra configurao interessante a do componente checkBoxPessoal. O binding da sua propriedade selected simples, mas observe que a propriedade pessoal da classe Contato sinttica e de somente leitura (Listagem 2). Um contato considerado pessoal se ele no tem empresa associada. Observe como essa regra faz com que o setter da propriedade empresa precise tambm notificar uma possvel mudana da propriedade pessoal. Com isso, se o usurio selecionar uma empresa para um contato, o JCheckBox automaticamente. Finalmente, observe que os componentes de entrada de dados do contato, assim como o boto Apagar, tm um binding configurado da propriedade enabled para a expresso ${not empty model.contatoSelecionado}. Isso faz com que os campos e o boto s estejam habilitados para edio/excluso quando existir alguma linha selecionada na tabela. Com todas estas ligaes aplicadas, a tela est pronta para espelhar os estados definidos no presentation model. O estado inicial da tela ser determinado pelo construtor do presentation model. Neste construtor (Listagem 4), importante perceber a forma como as listas so inicializadas. Observe que a lista de contatos inicializada com a criao de uma lista observvel atravs da classe ObservableCollections, disponvel na API do Beans Binding. Com ela, podemos criar listas capazes de notificar alteraes de estado e assim possibilitar a sincronizao dela com componentes como o JTable. Assim, cada vez que adicionarmos um objeto nesta lista, a tabela em nossa tela ganhar automaticamente uma nova linha. A lista de empresas no precisa ser observvel em nossa tela, uma vez que tratada como uma lista esttica. A ltima coisa necessria para configurar na nossa tela so as aes dos botes. Para simplificar este exemplo, clicamos duas vezes em cada boto para o NetBeans criar as aes automaticamente. Usando os conceitos do presentation model, as aes das telas devem ser extremamente simples. No nosso exemplo elas apenas delegam as chamadas paras os mtodos novoContato() e excluirContato() do presentation model. O mtodo novoContato() instancia um novo objeto Contato, seta alguns valores padro e o adiciona na lista de contatos. Assim, cada vez que esse boto for clicado, a tabela ganha uma nova linha. O mtodo excluirContato() simplesmente exclui da lista o objeto representado pela propriedade contatoSelecionado. Assim, quando esse boto clicado, a linha selecionada desaparece da tabela. Observe como a tela fica dinmica e interativa sem a necessidade de sujar as mos com muito cdigo de Swing. A classe do Presentation Model no tem nenhuma dependncia do pacote javax.swing e pode at mesmo ser usada numa verso em JSF desta mesma tela sem qualquer alterao. O binding cria efeitos interessantes como observar o dado na tabela ser alterado medida que se digita nas caixas de texto. As prprias clulas da tabela so editveis, refletindo as atualizaes nos campos de texto. Observe tambm como o binding consegue detectar mudanas como da propriedade telefone no Contato. Na Listagem 2 vemos que o setter desta propriedade processa o valor textual, adicionando um prefixo "0-XX-" que exibido automaticamente na tela aps a edio. sempre interessante abrir o cdigo gerado pelo NetBeans e tentar compreender como ele transforma nossas configuraes em chamadas para a API de Beans Binding. Afinal, temos que ter uma compreenso mnima de como as coisas funcionam para saber onde mexer quando algum problema acontecer. Finalmente, imagine o trabalho necessrio para fazer uma tela com este mesmo comportamento trabalhando sem Binding, ou usando solues Web do tipo AJAX. Isso um assunto para artigos futuros, mas imagine tambm como esse tipo de modelo se adapta ao uso de tecnologias como Hibernate. Podemos fazer uma query HQL que retorna um List de Contatos e fazer um binding diretamente do resultado desta consulta para um JTable. O usurio pode editar estes objetos e numa nica linha de cdigo podemos fazer um merge das mudanas e atualizar a base de dados. O presentation model normalmente o ponto de integrao com a camada de negcio do sistema, possuindo por exemplo, referncias a objetos de servios locais ou remotos. Concluses Java no desktop? Sim! Apesar da barreira tecnolgica certamente ser mais fcil de superar do que a psicolgica (aquela voz na cabea de todo arquiteto que parece sempre dizer a mesma coisa independente do cenrio: O sistema deve ter uma interface Web). Esse pequeno exemplo demonstra como o uso da API Beans Binding pode tornar o desenvolvimento de aplicaes de interface desktop uma tarefa mais simples e produtiva. Apesar de no estar livre de problemas (ver quadro Pedras no caminho), esta uma forma gil de criar interfaces desktop funcionais e interativas. E mesmo que voc no pretenda usar este tipo de abordagem, importante entender os conceitos que envolvem o binding de propriedades, pois tecnologias como a JavaFX mostram que o futuro aponta para isso.