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 {
String 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;
}
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;
}
}
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.