terça-feira, 23 de junho de 2009

JavaFX - conceitos básicos

Um pouco de background

O cenário para RIA's está bastante aquecido e JavaFX é a tecnologia desenvolvida pela Sun para disputar este mercado com Flash/Flex e Silverlight. Estas três tecnologias tem em comum o fato de serem disponibilizadas aos usuários através do browser porém executadas numa máquina virtual local. Desta forma é possível oferecer ao usuário interfaces muito mais elaboradas do que é possível com a combinação HTML+JavaScript (o cenário no qual trabalha JSF por exemplo).

A linguagem

Um dos principais diferenciais em relação ao Java é o estilo declarativo de codificação. Por exemplo, compare a forma Java de se instanciar um objeto do tipo SomeObject e sua dependência SomeObjectChild:
SomeObjectChild child = new SomeObjectChild();
child.setValue3(123);
SomeObject ref = new SomeObject();
ref.setValue1(123);
ref.setValue2("123");
ref.setChild(child);
ref.setSomeEventListener(new SomeEventListener() {
void someEvent() {
//do something
}
});
e a forma JavaFX de se instanciar um objeto equivalente:
def ref : SomeObject = SomeObject {
value1: 123
value2: "123"
child: SomeObjectChild {
value3: 123
}
someEvent: function(): Void {
//do something
}
}
O estilo declarativo é visivelmente mais claro e conciso.

Outra recurso interessante é chamado de binding. Através deste recurso é possível amarrar um atributo a outro. Desta forma toda vez que algum atributo é modificado todos os que estiverem amarrados são notificados. Na verdade é mais do que uma notificação, a expressão com a qual foi feita a amarração é executada novamente atualizando assim o valor do atributo amarrado. Vejamos alguns exemplos:
var stage : Stage;
var lbl: Label;

lbl = Label {
font : Font {
size : 20
}
width: 100

translateX: bind (stage.scene.width - lbl.boundsInLocal.width) / 2
translateY: bind (stage.scene.height - lbl.boundsInLocal.height) / 2
text: bind
if (stage.scene.width > 300) then
"LARGE!"
else
"thin..."

textFill: bind
if (stage.scene.width > 300) then
Color.RED
else
Color.BLACK
};

stage = Stage {
title: "Simple Binding"
width: 250
height: 80
scene: Scene {
content: [lbl]
}
}
A variável stage representa uma janela que será exibida para o usuário. Esta janela irá conter apenas um texto representado pela variável lbl. Repare que os atributos do texto estão definidos usando a palavra-chave bind. Isto irá fazer com que toda vez que as propriedades stage.scene.width e stage.scene.height forem modificadas os atributos text, textFill, translateX e translateY sejam calculados novamente. Na prática isto fará com que o texto seja movido para o centro da janela sempre que ocorrer um redimensionamento da mesma e também irá mudar o texto sendo exibido e sua cor de acordo com a largura da janela.

O recurso de binding é bastante poderoso e permite manter o estado dos componentes da janela sincronizados em torno de atributos chaves sem que para isso seja necessário definir listeners de eventos como seria feito numa interface Swing tradicional.

O exemplo acima não reflete isso mas este recurso facilita a construção da interface no padrão MVC. Fica bem simples sincronizar os componentes da View que exibem dados aos atributos do Model que armazenam estes dados. Desta forma ações (Controller) que impactam o Model irão automaticamente atualizar a View sem que isto fico explícito no código das ações. Portanto é possível alcançar um menor acoplamento entre estas camadas ficando de fato possível alterar componentes da View sem impactar as camadas de Model e Controller.

Mixins

Até a versão 1.1 do JavaFX existia suporte a herança múltipla. Este recurso foi eliminado na versão 1.2 em favor de "mixins". Este recurso permite que interfaces tenham uma implementação padrão de um ou mais de seus métodos. Desta maneira, uma classe qualquer que deve implementar uma interface referencia o mixin da interface ao invés da própria interface. Vejamos um exemplo simples composto de duas interfaces, dois mixins, uma classe abstrata e uma concreta:

Interfaces (Java):

public interface Persistable {
void save();
void delete();
}

public interface Identifiable {
String getIdentity();
}

Mixins (JavaFX):

public mixin class IdentifiableMixin extends Identifiable {
public override function getIdentity() : String {
return toString();
}
}

public mixin class PersistableMixin extends Persistable {
public override function save() : Void {
Persister.save(this);
}

public override function delete() : Void {
Persister.delete(this);
}
}

Classe abstrata (Java):

public abstract class Entity {
private Long id;

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}
}

Classe concreta (JavaFX):

public class SomePersistableIdentifiableEntity extends Entity, PersistableMixin, IdentifiableMixin {
public-init var field: String;

public override function toString() {
return field;
}
}

Desta maneira a classe SomePersistableIdentifiableEntity implementa as interfaces Persistable e Identifiable sem no entanto ter sido obrigado a implementar os métodos da interface pois foram utilizados as definições providas pelos "mixins". Caso necessário é possível sobreescrever os métodos para modificar algum comportamento. Graças a este recurso não foi preciso "sujar" a árvore de herança da classe com implementações dos métodos das interfaces. De uma maneira geral "mixins" promovem reuso de código e consistência nas chamadas a métodos de interfaces. Permitem que diversas classes que querem implementar determinada interface já ganhem de brinde uma implementação padrão de um ou mais de seus métodos.

Num próximo post sobre JavaFX iremos falar sobre novos modificadores de acesso a variáveis, operadores especiais para trabalhar com listas e mais.

2 comentários:

  1. Sei que é uma pergunta besta, mas ... as classes de JavaFX estendem as próprias classes do Java?! (no exemplo das interfaces) ...

    ResponderExcluir
  2. Sim, as classes e mixins do JavaFX podem estender classes Java ou implementar interfaces Java. Qualquer biblioteca Java existente pode ser utilizada com JavaFX.

    Vale notar no entanto que para permitir que uma aplicação JavaFX funcione sem problemas no ambiente desktop ou no ambiente móvel (celular etc) sem alterações no código é recomendado que sejam utilizadas versões JavaFX de componentes básicos da JRE. Por exemplo, deve ser usado javafx.util.Math ao invés de java.lang.Math.

    ResponderExcluir