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...