quarta-feira, 28 de novembro de 2007

Renderizadores e Binding

As JSRs 227, 273, 295 e 303 devem ser as linhas-mestre, mas, infelizmente, elas não estão prontas na totalidade e dificilmente terão um ponto comum. Assim, tenho que correr atrás de uma forma de fazer binding dos controles e dados no Merlin.

Ontem a noite, postei no grupo do merlin uma thread sobre renderizadores e binding customizados, com as idéias preliminares que eu tinha sobre isso. Porém, não sosseguei, e passei o dia hoje trabalhando no assunto. Fiz uma versão essencial, e o demo mostra o resultado.

As questões técnicas eu deixo em segundo plano, mas a essência é importante discutir.

Binding
Ao meu ver, o famoso binding pode ser interpretado como tarefa do controlador no modelo MVC, atuando entre a View e o Modelo (vejam como é difícil optar por uma língua única: português e ingles parecem viver juntos na Informática).

E ele pode ser ativado em momentos diferentes, várias vezes durante a interação do usuário. Essas várias vezes nada mais são que os eventos que a View (o controle da tela) dispara durante a interação com o usuário.

O binder (objeto que está fazendo o binding) pode ser ativado em um (ou vários) desses eventos, tanto para frente quanto para trás.

Para frente e para trás, podem ser interpretados como ações de "carregar o controle em função do valor do modelo" (load) e "atualizar o modelo em função do valor do controle" (update), respectivamente. Ou o contrário, é o freguês que manda :)

Pois bem, cada ação de load ou update, pode incorrer em sucesso ou fracasso. Geralmente, as ações de load (carregar o controle) não vão dar problemas no sistema, uma vez que é esperado que o modelo de dados esteja consistente. Porém, as ações de update podem, e geralmente vão, dar problemas. Isso porque é nesse momento que estamos capturando as CGDAS do usuário.

Quando uma CGDA dessas acontece, é função do binder avisar o sistema sobre o erro ocorrido. Assim, ele dispara uma mensagem com o que aconteceu. O controller captura essa mensagem e atualiza a view e o ciclo está finalizado.

Complexidades
Nota-se que tudo isso é um processo complexo, e que não pode ser fixo. Em outras palavras, por mais que tenhamos uma engenhosa miríade de código (tal como os validadores do Eclipse), precisamos ter uma porta de saída para o desenvolvedor. E essa porta de saída não pode ser a dos fundos.

A porta de saída precisa ser elegante, projetada e de fácil manuseio. É aí que entram as reais capacidades do framework e o porquê as JSRs acima.

A anotação RenderAs e o Binding
Como visto no post anterior, a anotação RenderAs serve para escolhermos um controle de tela diferente do padrão para um determinado campo de um objeto.

Quando ela é usada, é muito provável que estejamos saindo do contexto comum e caindo em casos onde precisamos, manualmente, dizer como o (mal)dito campo será mapeado para o controle da tela e vice-versa. É nesse momento que entra o binding.

Na implementação que fiz hoje (sofrendo com os genéricos do Java, diga-se de passagem), eu coloquei a feature do binding bem pertinho da anotação RenderAs. Tão pertinho, que ela está dentro, até. Vejamos o exemplo:

class UmCliente {

Strin
g nome;
@RenderAs(JTextField.class)
boolean vip = true;


}


Esse código produz algo como essa figura:


Notem que o campo vip parece ter sofrido um toString() para ser exibido no respectivo controle. E foi mais ou menos isso mesmo (pelo menos, nessa versão alpha do Merlin).

Para termos uma coisa mais elegante, podemos fazer algo como:

class UmCliente {

String nome;

@RenderAs(value=JTextField.class,binder=BooleanToTextComponent.class)
boolean vip = true;

}


Agora, além de especificar um controle de tela diferente, eu avisei o Merlin para colocar um binder específico. O resultado disso é algo como a figura:


Mas como esse binder é implementado? Vejam abaixo:

public class BooleanToTextComponentBinder implements IBinder[Boolean,JTextComponent] {

public boolean load(Boolean model, JTextComponent view) {


view.setText(model.booleanValue() == true ? "SIM" : "NÃO");
return true;


}

public boolean update(Boolean model, JTextComponent view) {


model = view.getText().equals("NÃO") ? Boolean.TRUE : Boolean.FALSE;
return true;


}

}

A interface genérica IBinder[M,V] (que o post mostra incorretamente, pois caracteres "maior que" e "menor que" não são suportados aqui) do Merlin, e especifica os métodos load e update, como discutido anteriormente. Graças aos tipos genéricos, não precisamos conversões para acessar tanto o modelo, quanto o controle de tela. E, no miolo dos métodos, código Java simples...

Conclusões
Não está tão simples quanto eu quero, mas para uma versão feita em três horas e meia está louco de bom (sim, o resto do dia foi perdido fazendo o vídeo-demo. Pode?)

Espero que das JSRs converjam (existe essa palavra?) logo, de forma que o primeiro release do Merlin esteja bem fundamentado nelas.

Por hora é isso.

Nenhum comentário: