quinta-feira, abril 19, 2007

Novas palestras da 3Layer

Ontem, estive finalizando alguns trabalhos internos pela empresa e fechando contatos. Entre os emails trocados, decidimos montar uma proposta de palestra para o nosso framewwork, o Merlin. Na prática, é a mesma palestra que apresentei no 8 Fórum Internacional de Software Livre na semana passada. Porém, como sempre estamos tentanto melhorar, vou colocar junto um demo feito com o Wink, para que a galera possa ver a coisa em funcionamento mesmo. De quebra, meu colega Júlio decidiu fazer uma apresentação sobre Java Server Faces também.

Beleza, fechamos as duas palestras com nosso parceiro e acho que em Maio próximo elas devem estar no circuito de eventos da região aqui em Porto Alegre.

terça-feira, abril 17, 2007

Construtores de Frameworks

Gosto quando vejo que nossos esforços rendem bons comentários. Mas o post de hoje é outro. É um interim entre o Merlin e os construtores de frameworks.

Frameworks, construtores e Leis
Construtores de frameworks devem seguir 3 regras, nessa ordem:
1. Tudo que fizerem deve ser extremamente flexível.
2. Tudo que fizerem deve ser extremamente configurável.
3. Tudo que fizerem como padrão deve ser extremamente genérico.

Aos que ignorarem uma das leis, o fracasso é iminiente. Dito isso, voltamos ao Merlin.

Dependências entre controles, o conceito
Uma funcionalidade importante nas telas de cadastro é a dependência entre controles de tela. Por exemplo, ao marcar um checkbox, um texto deve ser habilitado; ou ao escolher um item em uma combo, outra deve ser preenchida com base nos valores da primeira.
Existem duas formas de fazer isso. Uma delas é automática, usando heurísticas. A outra é manualmente, através de anotações de dependência. As heurísticas são uma longa história de devo abordar em outro post. O que vou falar agora são das anotações de dependência.

Dependências entre controles, usando anotações para criá-las
A anotação @Dependence é a meta-informação que define uma dependência entre controles no Merlin. Sua assinatura é:

public @interface Dependence {
String value();
boolean opposite() default false;
Class[? extends IActionDependence] action() default Enablement.class;
}

Em value, indicamos os atributos dependentes, separadores por ponto-e-vírgula. Também pode ser o nome de um agrupamento ou de controle de tela que nem seja renderizado pelo Merlin. Já opposite indica que a dependência será invertida em relação à lógica normal (dou exemplo abaixo). A action nada mais é do que a operação a ser executada para contemplar a regra de dependência. Por padrão essa ação é a habilitação/desabilitação de controles. Nota-se que essa ação necessariamente precisa ser um subtipo de IActionDependence, que é justamente a interface que define o comportamento para ações de dependência. Se olharmos a declaração dessa interface, temos o seguinte:

public interface IActionDependence {
public void execute(Object source, Object destination, boolean opposite);
public List[Class[?]] operatesOver();
}

Observa-se que ela possui um método de execução, que recebe como parâmentro o objeto de origem, o destino e a informação de inversão de execução da regra. Esses valores são preenchidos pelo Merlin automaticamente com base nos valores anotados. O método operatesOver é um facilitador e pode retornar nulo sem problemas. Seu objetivo é permitir que IDEs utilizem seu retorno para fazerem parse em tempo de projeto e exibir warnings quando o tratador de dependência indicado pela ação não contemplar o controle de destino (auauauau). Em outras palavras, se eu colocar uma dependência para um controle JXPanel e o meu tratador não operar sobre JXPanel...

Para usar a anotação de dependência, temos o seguinte exemplo:

class Cliente {
@Dependence("empresa")
boolean trabalhando;
Empresa empresa;
}

Nesse caso, é criada uma dependência padrão que indica que ao selecionar o checkbox(1) trabalhando, a combo(1) empresa será habilitada para preenchimento.

Esse comportamento é assim porque @Dependence tem como padrão a classe Enablement, que nada mais faz do que habilitar ou desabilitar controles conforme a seleção ou não da origem. Ao olhar a classe Enablement notamos que ela efetivamente estende IActionDependence e seu método operatesOver retorna na lista de aplicações o controle do tipo checkbox, que é o padrão para booleanos como trabalhando.

Dependências entre controles, estendendo o padrão
Enablement é uma ação de dependência padrão do framework. Outra ação de dependência do framework é a Visibility. Ela nada mais faz do que mostrar ou ocultar o(s) controle(s) dependente(s).
Embora essas ações sejam simples e genéricas, muitas vezes precisaremos ações complexas, onde a habilitação ou não de um controle vai depender da execução de infindáveis regras de negócio, por exemplo. Uma abordagem é utilizar agentes (?) para fazer isso. Porém, é viável que seja criada um tipo de dependência para isso. Por exemplo, todas as ações de dependência precisam validar o usuário corrente do sistema. Nesse caso, ao invés de atachar agentes com eventos e tratadores em vários lugares, podemos criar dependências customizadas. Para implementar são necessários dois passos:
1. Cria-se uma classe que implementa IActionDependence.
2. Utiliza-se a anotação @Dependence apontando para a ação que é nossa classe customizada.

Vamos ao exemplo:

//Permite habilitar controles se o usuario for administrador
class MySuperSpecialEnterpriseEnablementDependence implements IActionDependence {

public void execute(Object source, Object destination, boolean opposite) {
JCheckBox origem = (JCheckBox) source;
JComponent destino = (JComponent) destino;
if (Application.getContext().getUser().getRoles().contains("admin")) {
destino.setEnabled(origem.isEnabled());
return;
}
destino.setEnabled(false);
}
public List[Class[?]] operatesOver() {
return Arrays.asList(JCheckBox.class);
}
}

Feita a classe customizada, basta usar:

class Cliente {
@Dependence(value="empresa", action=MySuperSpecialEnterpriseEnablementDependence.class
boolean trabalhando;
Empresa empresa;
}

Agora temos uma dependência customizada, que permite somente a usuários admistratores terem a empresa habilitada ao clicar em trabalhando. Uma regra inútil e um péssimo exemplo, eu sei :-)

De volta às Leis
Meu exemplo é ignóbil. E é uma pena. Mas felizmente, acho que as regras fundamentais dos contrutores de framworks eu consegui seguir. Quanto a ser flexível, o Merlin deixa em aberto, para livre implementação, qualquer tipo de dependência entre quaisquer controles, desde os renderizados (já falei que adoro esse termo?!) por ele, até aqueles criados na mão. Quanto a ser configurável, acredito que a possibilitadade de atachar vários controles na dependência com pontos-e-virgulas é uma mão na roda. De quebra, o operatesOver já vem para dar suporte a plugins em IDEs. Quanto a ter um padrão que cobre os casos mais genéricos, acredito que suportando de cara a visibilidade e a habilitação de controles já é de bom tamanho. Optar por Enablement como padrão nesses casos também me parece o mais coerente, certo?

Assim termino o post de hoje, com um monte de dependências entre os neurônios.

Notas
(1) Esses são os controles padrões renderizados para esses tipos de dado. Outros podem ser escolhidos com anotações específicas.
(2) Notem que usei colchetes ao inves de maior que e menor que; isso porque o editor do Google simplemente corta fora esses últimos caracteres.

domingo, abril 15, 2007

Inferência de navegabilidade: um problema de controle

Uma funcionalidade importante e necessária para sistemas que operam sobre telas um-para-X (1-1,1-n) é permitir a navegação entre diferentes interfaces (cadastros) relacionadas. Pode não ser especificamente um JFrame (ou outro assemelhado), mas um panel, um dialogo, uma aba, não importa. O que importa é possibilitar que, a partir de um registro (chamado Mestre) possamos navegar para outro (chamado Filho). É o típico conceito de mestre-detalhe.

Esse tipo de conceito foge à alçada do Merlin. Por que? Porque isso é um caso de controle. É o típico caso de aplicabilidade de frameworks web como o Struts. Para desktop, bisbilhotando nas JSRs, ainda não encontrei nada. É uma pena.

Assim, enquanto o padrão não surge, precisamos dar um Norte para o assunto, ou seja, permitir que telas Mestre-Detalhe também sejam suportadas pelo Merlin. Assim, vamos aos estudos teóricos (...)

Diversas ferramentas existentes que operam sobre o conceito de geração baseada em modelos advogam que as navegacoes podem ser inferidas a partir dos links existentes entre os objetos da aplicação (...)

Assim, caso eu tenha um relacionamento um-para-muitos entre um cliente e seus veículos, podemos inferir que a tela de cadastro de cliente será um mestre-detalhe entre Cliente e Veiculo. Isso é fácil e simples. Não tem nenhuma maravilha em "descobrir" isso.

Mas a questão vem da usabilidade. Digamos que o usuário cadastrou o cliente e editou alguns de seus veículos e, daí, também (e diretamente), ele deseja modificar alguns dos seus telefones (...)

Nesse caso, é importante que exista uma forma de navegar do cadastro de veículos para o cadastro de telefone sem ter que voltar para o cadastro de cliente. Mas se olharmos diretamente para as relações no modelo, vamos perceber que não existe ligação direta entre Veiculo e Telefone.

Assim, uma heurística para resolver isso é a seguinte: permitir a navegação entre filhos de mesmo pai. Olhando essa figura, percebemos que os fluxos inferidos (em azul) são exatamente isso.

Esse tipo de comportamento pode ser bastante útil e pode ser implementado de várias formas. O detalhe é, friso novamente friso, que isso não é problema tela de cadastro, mas sim navegação, de controle.

Implementar isso no Merlin pode ser perigoso. Não implementar pode ser um ponto para usuários berrarem que a ferramenta é incompleta. Ao meu ver não é o caso, mas vou pensar no assunto.