Swing Fácil com Groovy: Como criar seus próprios componentes
Não tenho dúvidas que o desenvolvimento de aplicações desktop com Java tem seu problemas. Não digo apenas pela estrutura de composite do Swing, ou de classes gigantes com muitas responsabilidades como o JFrame. Mas mesmo com todos esses problemas vejo o desenvolvimento de aplicações Swing para Java ainda com viabilidade.
Hoje é dia é comodismo desenvolver aplicações para Web. O que acontece é que nem todos os sistemas ou nem todas as funcionalidades dos sistemas podem ser feitas na web, por que?
Como deixar o Swing melhor?
A melhor coisa seria um refatoração e re-design da API mas como isso não é simples e mesmo que feito iria dar muito custo e problemas de manutenção/migração. Então qual seria uma possibilidade intermediária? Groovy!
O Groovy tem um recurso muito bom que é o suporte a Builders que são classes que seguem o pattern Builder. Com esse objetos podem criar objetos de maneira muito mais fácil e produtiva, sem falar que o código fica muito mais simples por se parecer com uma DSL.
Por que usar um Builder?
A questão não é só clareza, simplicidade e produtividade, com um Builder vamos alem disso. Um Builder é como um factory só que parametrizavel, logo se você constrói suas interfaces com Builders do Groovy ao invés de Swing direto você ganha a possibilidade mudar o comportamento da interface sem ter que mudar todas as telas!
Como isso é possível?
Isso é possível devido a duas coisas que se usadas em conjunto vão dar a você a flexibilidade e robustez necessárias a suas aplicações desktop. A primeira delas é fazer hold da variável de builder no seu código para uma classe mais superior. Aqui estou me referindo a boa e velha programação para interfaces, isso é bem possível de se fazer com groovy por que existe o BuilderSupport.
A única coisa que para mim deixou a desejar foi no design do SwingBuilder por que esse cara não segue a mesma hierarquia de classes do BuilderSupport. Bom até a versão 1.5.7 é assim vamos ver se agora nas próximas versões os caras da SS resolvem isso :)
Criando os seus próprios Builders
Criar um builder é uma tarefa relativamente fácil a questão é a complexidade que você irá colocar no seu componente. Vou mostrar como criar um builder de gui que extende o SwingBuilder e adiciona um novo widget que gera uma caixa de texto que só permite digitação de números!
Dependências
CustomSwingBuilder.java
Esse é o Builder fato. Ele extende o SwingBuilder e tem a sobre-escrita do método registerWidgets esse método registra as factories que tem os widgets personalizados que você venha a construir!
Neste caso por uma questão de boas práticas estou passando uma constante com a tag do widget que no caso tem o valor "caixaNumeros", que sera a tag que você utilizara no groovy.
Agora vamos ao código da factory NumericTextFactory.java
Hoje é dia é comodismo desenvolver aplicações para Web. O que acontece é que nem todos os sistemas ou nem todas as funcionalidades dos sistemas podem ser feitas na web, por que?
- Complexidade do desenvolvimento com JavaScript
- Consumo de memória do cliente(Browser)
- Complexidade de tecnologias Ajax VS problemas de uso de Arquiteturas Commet.
- Falta de bons IDEs e Debuggers para JavaScript
- Pouca performance na execução de JS devido a sua arvore DOM.
- Tempo de desenvolvimento e custo de manutenção
Como deixar o Swing melhor?
A melhor coisa seria um refatoração e re-design da API mas como isso não é simples e mesmo que feito iria dar muito custo e problemas de manutenção/migração. Então qual seria uma possibilidade intermediária? Groovy!
O Groovy tem um recurso muito bom que é o suporte a Builders que são classes que seguem o pattern Builder. Com esse objetos podem criar objetos de maneira muito mais fácil e produtiva, sem falar que o código fica muito mais simples por se parecer com uma DSL.
Por que usar um Builder?
A questão não é só clareza, simplicidade e produtividade, com um Builder vamos alem disso. Um Builder é como um factory só que parametrizavel, logo se você constrói suas interfaces com Builders do Groovy ao invés de Swing direto você ganha a possibilidade mudar o comportamento da interface sem ter que mudar todas as telas!
Como isso é possível?
Isso é possível devido a duas coisas que se usadas em conjunto vão dar a você a flexibilidade e robustez necessárias a suas aplicações desktop. A primeira delas é fazer hold da variável de builder no seu código para uma classe mais superior. Aqui estou me referindo a boa e velha programação para interfaces, isso é bem possível de se fazer com groovy por que existe o BuilderSupport.
A única coisa que para mim deixou a desejar foi no design do SwingBuilder por que esse cara não segue a mesma hierarquia de classes do BuilderSupport. Bom até a versão 1.5.7 é assim vamos ver se agora nas próximas versões os caras da SS resolvem isso :)
Criando os seus próprios Builders
Criar um builder é uma tarefa relativamente fácil a questão é a complexidade que você irá colocar no seu componente. Vou mostrar como criar um builder de gui que extende o SwingBuilder e adiciona um novo widget que gera uma caixa de texto que só permite digitação de números!
Dependências
- JDK 1.6
- Groovy 1.5.7 (groovy-all-1.5.7.jar)
- Criar o Builder
- Criar uma factory
- Registrar essa factory no builder
CustomSwingBuilder.java
package com.blogspot.diegopacheco.groovy.builder; import groovy.swing.SwingBuilder; import com.blogspot.diegopacheco.groovy.builder.factories.NumericTextFactory; /** * Builder groovy que adiciona funcionalidades ao builder do Swing. * * @author Diego Pacheco * @since 20/01/2009 * @version 1.0 * */ public class CustomSwingBuilder extends SwingBuilder{ public CustomSwingBuilder() { super(); } /** * Método que registra as factories que vao processar os widgets adicionados por você. * Você deve informar a tag do widgets e a factory que processa o mesmo. * */ @Override protected void registerWidgets() { super.registerWidgets(); registerFactory(NumericTextFactory.WIDGET_TAG, new NumericTextFactory()); } }
Esse é o Builder fato. Ele extende o SwingBuilder e tem a sobre-escrita do método registerWidgets esse método registra as factories que tem os widgets personalizados que você venha a construir!
Neste caso por uma questão de boas práticas estou passando uma constante com a tag do widget que no caso tem o valor "caixaNumeros", que sera a tag que você utilizara no groovy.
Agora vamos ao código da factory NumericTextFactory.java
package com.blogspot.diegopacheco.groovy.builder.factories; import groovy.util.AbstractFactory; import groovy.util.FactoryBuilderSupport; import java.awt.Color; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.util.Map; import javax.swing.BorderFactory; import javax.swing.JTextField; /** * Factory que cria um JLabel com controle de digitação * que soh deixa digitar numeros. * * @author Diego Pacheco * @since 20/01/2009 * @version 1.0 * */ public class NumericTextFactory extends AbstractFactory{ public static String WIDGET_TAG = "caixaNumeros"; @Override @SuppressWarnings("unchecked") public Object newInstance(FactoryBuilderSupport builder, Object name,Object value, Map attributes) throws InstantiationException, IllegalAccessException { JTextField textField = new JTextField((String)attributes.get("text")); textField.setBackground(Color.YELLOW); textField.setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY)); textField.setToolTipText("Digite algum número entre 0 e 9!"); attachEvents(textField); return textField; } /** * Metodo que adiciona eventos ao botao * @param textField */ private void attachEvents(JTextField textField){ textField.addKeyListener(new KeyListener(){ public void keyPressed(KeyEvent e){} public void keyReleased(KeyEvent e){} public void keyTyped(KeyEvent e){ System.out.println(e.getKeyChar()); if ( (e.getKeyChar() >=48 && e.getKeyChar()<=57)){ // numeros validos }else{ e.consume(); } } }); } }Aqui que as coisas acontecem! Essa classe extende AbstractFactory que é a factory padrão dos builders do Groovy ela já contem os métodos do ciclo de vida do parser do Groovy, assim você tem um conjunto de métodos estilo callback a sua disposição. Nesse caso só foi necessário o uso do método newInstance o qual cria um objeto JTextField e chamo um metodo privado que eu criaei para colocar o tratamento de eventos. O tratamento de evento só deixa que se digite números de 0 a 9. Agora podemos testar o builder em um código Groovy chamei de GroovyUsage.groovy
package com.blogspot.diegopacheco.groovy.builder.usage import java.awt.* import javax.swing.* import groovy.swing.* import com.blogspot.diegopacheco.groovy.builder.* /** * Classe groovy que mostra como usar o builder. * * @autor Diego Pacheco * @since 20/01/2009 * @version 1.0 * */ public class GroovyUsage { def swing = new CustomSwingBuilder() public void renderMe(){ def frame = swing.frame(title:'Tela de Teste', size:[300,300]) { borderLayout() caixaNumeros(text:"",constraints: BorderLayout.NORTH) } frame.show() } public static void main(String []args){ new GroovyUsage().renderMe() } }Aqui podemos ver que esta sendo usado a tag caixaNumeros(text:"",constraints: BorderLayout.NORTH) e mais nada! Se você rodar esse código ira aparecer uma tela com uma caixa de texto que só permite a digitação de números! Ese exemplo é muito simples! O importante aqui é as possíbilidades que esse recurso do Groovy nos permite dentre as muitas possíbilidades podemos:
- Criar componentes customizados e usa-los de forma produtiva
- Colocar controles arquiteturais do tipo segurnça, i18m, etc...
- Requisitos de usabilidade como cores, posicionamento, fonte, etc...