quarta-feira, 8 de janeiro de 2014

Data Binding

Olá, este post tem como função explicar o Data Binding, como usar e o por que. Então, lá vamos nós.

Data Binding é um processo que estabiliza uma conexão entre uma aplicação User Interface(JFrame) e a lógica do negócio. Se as configurações e notificações estiverem corretas, o Data Binding reflete as mudanças que elas fazem. Vamos supor que temos um AlunoFrame e nela um componente JTextField tNome, também temos a classe Aluno com o getNome e setNome. Se nós aplicarmos o Data Binding vamos fazer que quando seja digitado algo no TextField nome, automaticamente o setNome vai receber o que foi escrito, eles ficam sincronizados, as mudanças que ocorrem no nome ocorrem também no setNome e vice-versa. Mas então tu podes pensar, "por que não usar um setNome neste TextField?".

public class AlunoFrame extends javax.swing.JFrame{
     Aluno aluno = new Aluno();
     JTextField nome = new javax.swing.JTextField();
     /...

      private void tNomeActionPerformed(java.awt.event.ActionEvent evt){
         aluno.setNome(tNome.getText());         
     }

Podemos ver que fazendo assim não é nem um pouco elegante, deixamos o usuário com muita acessibilidade. Se por acaso ele quiser alterar para setMatricula ele pode, e não é isso que nós queremos. Nós temos que abstrair o usuário sobre o que está acontecendo. E também vale lembrar que para fazer espelhamento, deste modo fica muito "amador". Por isso vamos usar o Data Bindig, agora na prática vai ficar bem mais visível sua utilidade. Então vamos criar uma classe Bind que será a responsável por fazer a sincronização do JFrame com o negócio, so let's go.

public class Bind extends JTextField{    

    public Bind() {
        
    } 
    //criamos um método bind que vai receber os seguintes parametros
    //obj que será o objeto da classe que queremos alterar
    //atributo que será qual o atributo da classe que queremos mudar
    //campoFrame que é o que queremos onde a alteração seja feita
    //texto é a informação que vamos passar
    public void bind(Object obj, String atributo, Object campoFrame, String texto) throws NoSuchMethodException, IllegalAccessException...{

        //aqui nós vamos ver qual tipo é o campoFrame 
        //e depois repassamos para seu método correspondente
        if(campoFrame instanceof JTextField){
            JTextField tf = (JTextField) campoFrame;
            bindTextField(obj, atributo, tf, texto);
            return;
        }

        //exemplo com outros tipos de componentes
        if(campoFrame instanceof JComboBox){
            JComboBox cb = (JComboBox) campoFrame;
            bindComboBoxBinder(obj, atributo, cb, texto);            
            return;
        }
        if(campoFrame instanceof JRadioButton){
            JRadioButton rb = (JRadioButton) campoFrame;
            bindRadio(obj, atributo, rb, texto);
            return;
        }        
    }
Após implementar essa parte, nós precisamos fazer o bindTextField que terá como função ir lá no set da classe do objeto passado e alterar. Essa parte eu admito que é um pouco mais chatinha e complexa de entender pois vamos usar reflections e regex, então vou explicar por partes.

private void bindTextField(Object obj, String atributo, JTextField tf, String texto) throws NoSuchMethodException, IllegalAccessException... {
        tf.setText(texto);       
        
        //aqui nós vamos precisar usar reflections para saber os atributos
        //da classe do objeto que temos

        Field[] f = obj.getClass().getDeclaredFields(); //criamos um vetor de Field para pegar os atributos
        Field field = null; 
        
        //fazemos um for each para pesquisar dentro daquele nosso vetor 
        //para saber pegarmos o atributo que queremos alterar             
        for(Field campo : f){
            if(campo.getName().equals(atributo)){ //enquanto a variavel campo não for igual ao atributo continua o for
                field = campo; //a variavel field recebe o campo quando achado
                break;
            }
        }
        //agora que já achamos o atributo, nós precisamos encontrar o set dele
        //ATENÇÃO AGORA
        //Vamos criar uma String s que irá concatenar a palavra set com 
        //a primeira letra do atributo em maiuscula e o restante em minuscula
        String s = "set" + field.getName().toUpperCase().charAt(0)+ field.getName().substring(1);

        //no nosso exemplo fica s = "set"+N+ome
Depois que "formamos" o setNome, nós precisamos achar ele nos métodos da classe e fazer a alteração.
String tipo = field.getType().getSimpleName();//aqui criamos uma variavel tipo que irá
                                                      //pegar o nome do Tipo do campo field(que é nome)

        Method meth = null; //é criada uma variavel do tipo Method que será usada 
                            //para chegar ao método setNome
        
        //é feito um switch para ver qual é o Tipo do nosso atributo que será atualizado
        switch(tipo){
            case "String":
              //meth = aluno.getClass().getMethod(setNome, tipo String);
                meth = obj.getClass().getMethod(s, String.class);
                //usamos o metodo invoke para passar o objeto e a nova informação
                meth.invoke(obj, texto);
                break;
            case "int": case "Integer":
                meth = obj.getClass().getMethod(s,Integer.TYPE);
                meth.invoke(obj, Integer.parseInt(texto));
                break;
            case "float": case "Float": 
                meth = obj.getClass().getMethod(s,Float.TYPE);
                meth.invoke(obj, Float.parseFloat(texto));                
                break;
            case "double": case "Double":
                meth = obj.getClass().getMethod(s,Double.TYPE);
                meth.invoke(obj, Double.parseDouble(texto));  
                break;
            case "booelan": 
                Boolean.parseBoolean(texto);
                break;
            case "Date":
                 //OBSERVAÇÃO COM O DATE
                 //d{2}dias / d{2} mes / d{4} ano
                Pattern pattern = Pattern.compile("\\d{2}\\/\\d{2}\\/\\d{4}");
                        
                if(pattern.matcher(texto).find()){
                    SimpleDateFormat data = new SimpleDateFormat("dd/MM/yyyy");
                    meth = obj.getClass().getMethod(s, Date.class);
                    meth.invoke(obj, data.parse(texto));
                    
                }
                break;
        }       
    }
O case Date vou explicar por aqui pois é o mais complicado deles. Se por caso o nosso aluno tenha dataDeNascimento do tipo Date, vai ser necessário usar regex para formatar a data conforme for configurada pelo usuário, apartir do SimplesDateFormat(esqueçam todas gambiarras feitas até então). Criamos uma variável do tipo Pattern, quando colocamos o d{2} dizemos que aquela parte recebe até dois decimais. Mais a baixo temos um SimplesDateFormat que está separando por dia, mês e ano. Após isso, é igual aos outros.

Aqui terminamos nossa classe Bind, agora vamos ver como usar ela. No nosso formulário, no campo tNome nós precisamos usar o evento KeyReleased, fica da seguinte forma.

private void tNomeKeyReleased(java.awt.event.KeyEvent evt) {                                  
        Bind t = new Bind();
        t.bind(aluno2, "nome", tCampoParaMostrar, tNome.getText());
}

Admito que demorei para pegar a ideia do Data Binding, para entender como funciona essa sincronização via objeto, mas não é complicado de fazer, só é necessário tempo. Sobre reflections e regex, a ideia é bem fácil de entender.

Nenhum comentário:

Postar um comentário