Sistemas de Informação
PEDERNEIRAS – SP
2006
FGP-FACULDADE GENNARI & PEARTREE
Sistemas de Informação
Orientadora: Vânia
PEDERNEIRAS – SP
2006
FOLHA DE APROVAÇÃO
Orientadora:
Examinador:
Examinador:
LISTA DE ILUSTRAÇÕES
1 INTRODUÇÃO.................................................................................................................................8
1.1. Nome do framework.................................................................................................................8
1.2. Descrição do problema.............................................................................................................8
1.2.1. Limitações do MVC..........................................................................................................9
1.2.2. Origem do MVP..............................................................................................................10
1.3. Objetivos do Surf Framework.................................................................................................10
1.4. Procedimentos de instalação...................................................................................................11
1.4.1. Requisitos básicos...........................................................................................................11
1.4.1.1. Requisitos de hardware...........................................................................................11
1.4.1.2. Requisitos de software.............................................................................................11
1.4.2. Procedimento para instalação..........................................................................................11
1.4.3. Procedimento para configuração.....................................................................................11
1.4.3.1. Eclipse 3.2...............................................................................................................12
1.4.3.2. NetBeans 5.5...........................................................................................................12
1.4.4. Aplicativo de demonstração............................................................................................12
2 DESCRIÇÃO DO FUNCIONAMENTO........................................................................................13
2.1. Arquitetura do framework......................................................................................................13
2.1.1. Models e View................................................................................................................13
2.1.2. Swing..............................................................................................................................13
2.1.3. Configurações do Surf....................................................................................................14
2.1.3.1. Adaptadores de componentes..................................................................................14
2.1.3.2. Conversores.............................................................................................................14
2.1.3.3. Formatadores...........................................................................................................14
2.1.4. Configurações do Presenter............................................................................................14
2.1.5. Binding............................................................................................................................14
2.1.6. Commands......................................................................................................................15
2.1.7. Presenter..........................................................................................................................16
2.2. Desenvolvendo a primeira aplicação......................................................................................16
2.2.1. Construindo a aplicação..................................................................................................18
2.2.1.1. Criando o model......................................................................................................18
2.2.1.2. Criando a view.........................................................................................................19
2.2.1.3. Criando o presenter.................................................................................................20
2.2.1.4. Criando a classe principal........................................................................................21
3 CONHECENDO O SURF FRAMEWORK....................................................................................23
3.1. Configurando o Surf...............................................................................................................23
3.1.1. Configuração manual......................................................................................................23
3.1.2. Configuração via Spring.................................................................................................24
3.1.3. Configuração via Pico.....................................................................................................26
3.2. Nested properties....................................................................................................................27
3.2.1. Referenciando um atributo aninhado..............................................................................27
3.3. Anotações do Surf...................................................................................................................28
3.3.1. Apresentando as anotações.............................................................................................28
3.3.1.1. Anotações de view...................................................................................................28
3.3.1.1.1. @Bind..............................................................................................................28
3.3.1.1.2. @Formatter......................................................................................................29
3.3.1.1.3. @Interaction....................................................................................................30
3.3.1.1.4. @InteractionList..............................................................................................32
3.3.1.1.5. @Property........................................................................................................33
3.3.1.1.6. @SubView.......................................................................................................34
3.3.1.1.7. @Type.............................................................................................................36
3.3.1.1.8. @Widget..........................................................................................................37
3.3.1.2. Anotações de presenter............................................................................................37
3.3.1.2.1. @Model...........................................................................................................37
3.3.1.2.2. @OnViewActivate..........................................................................................38
3.3.1.2.3. @OnViewClose...............................................................................................39
3.3.1.2.4. @OnViewClosed.............................................................................................39
3.3.1.2.5. @OnViewDeactivate.......................................................................................39
3.3.1.2.6. @OnViewDeiconify........................................................................................40
3.3.1.2.7. @OnViewGainFocus.......................................................................................40
3.3.1.2.8. @OnViewIconify............................................................................................40
3.3.1.2.9. @OnViewLostFocus.......................................................................................40
3.3.1.2.10. @OnViewOpened..........................................................................................41
3.3.1.2.11. @Presenter.....................................................................................................41
3.3.1.2.12. @View...........................................................................................................41
3.3.2. Mais exemplos de uso das anotações..............................................................................42
3.4. Criando os models..................................................................................................................42
3.5. Criando as views.....................................................................................................................42
3.6. Criando os presenters..............................................................................................................42
3.6.1. Definindo os models.......................................................................................................43
3.7. Criando commands.................................................................................................................43
3.7.1. Combinando generics com commands...........................................................................44
3.7.2. Criação simplificada de commands................................................................................45
3.8. Command Context..................................................................................................................46
3.9. Utilizando o recurso de data binding......................................................................................48
3.9.1. Sincronizando a view e o model.....................................................................................49
3.9.2. Trabalhando com enums.................................................................................................50
3.10. Operações comuns................................................................................................................51
3.10.1. Obtendo uma referência a um componente da View....................................................52
3.10.2. Habilitando e desabilitando componentes.....................................................................52
3.11. Componentes especiais.........................................................................................................52
3.11.1. Componente JList.........................................................................................................52
3.11.2. Componente JTable.......................................................................................................53
3.11.3. Componente JRadioButton...........................................................................................55
3.12. Trabalhando com seleções....................................................................................................56
4 SEGUNDA APLICAÇÃO: GERENCIADOR DE TAREFAS......................................................58
4.1. Criação do model....................................................................................................................59
4.2. Interfaces dos presenters.........................................................................................................60
4.3. Implementação do presenter da janela principal.....................................................................61
4.4. Implementação do presenter da janela de manutenção...........................................................64
4.5. Criação dos commands...........................................................................................................65
4.6. Criação das views...................................................................................................................67
4.6.1. Criação da view principal...............................................................................................67
4.6.2. Criação da view de manutenção......................................................................................69
4.7. Configuração da camada intermediária..................................................................................70
4.7.1. Utilizando o Spring.........................................................................................................70
4.7.2. Utilizando o Pico.............................................................................................................72
4.8. Iniciando a aplicação..............................................................................................................74
5 CONSIDERAÇÕES FINAIS..........................................................................................................75
6 TRABALHOS FUTUROS..............................................................................................................76
7 REFERÊNCIAS BIBLIOGRÁFICAS............................................................................................77
8
1 INTRODUÇÃO
• Model: São informações que indicam o estado do componente, como, por exemplo, o
texto de um campo de texto ou a indicação de que um ckeckbox está marcado ou
desmarcado;
• View: Acessa os dados do Model e especifica como os seus dados são exibidos ao
usuário;
• Controller: Componente cuja responsabilidade é mapear as ações do usuário na
View, as quais ocorrem através de eventos, e modificar o Model de acordo com o
evento disparado. Para citar um exemplo, quando um o usuário modifica o conteúdo
de um campo de texto, o Controller intercepta o evento de pressionamento de teclas
disparado na View, alterando o Model com o novo valor.
1 O Smalltalk foi uma das primeiras linguagens Orientadas a Objetos que surgiram e o Model-View-Controller
(MVC) foi um framework de criação de interfaces com o usuário desenvolvido para Smalltalk.
9
Para ilustrar melhor essa situação, vamos tomar como exemplo um formulário que
sirva para cadastrar clientes. A View é o formulário, cuja responsabilidade é agrupar os
componentes visuais que representam os dados do cliente e fornecer um meio para que o
usuário interaja com o sistema. O Model são os objetos Cliente, cuja responsabilidade é
armazenar os dados de um cliente e prover a funcionalidade necessária para que sejam
satisfeitos os requisitos da aplicação. O Controller, por sua vez, é responsável por observar a
interação do usuário com o formulário (View) e traduzir essa interação em eventos, que
servem para controlar o fluxo da aplicação e modificar o estado dos controles da janela,
atualizando também os objetos de negócio (Model).
Podemos concluir então que, ao aplicar o padrão MVC, temos informações pertinentes
à lógica de exibição dos dados codificadas diretamente no Model, ou seja, o Model deve
conter código responsável por notificar a View assim que o seu estado é modificado pelo
Controller. A ausência de um limite bem definido entre os componentes faz com que
determinadas funções e responsabilidades sejam inseridas nos lugares errados, tornando o
código mais difícil de reaproveitar, testar e modificar.
Apenas para concluir o raciocínio, pode-se dizer que não é desejável que a classe
Cliente, do exemplo anterior, implemente alguma interface ou estenda alguma classe que não
seja relacionada ao negócio que ela se propõe a resolver. Além disso, o Model não deve saber
quando ou como as informações devem ser repassadas para a View.
2 O padrão Subject/Observer define a dependência um-para-muitos entre objetos para que quando um objeto
mude de estado, todos os seus dependentes sejam avisados e atualizados automaticamente. [GAMMA, 2000]
10
Como mostra a ilustração 2, no padrão MVP, o Model não interage com a View,
enquanto que, no padrão MVC, o Model é responsável por notificar a View quando seu
conteúdo é modificado. No MVP, a View também não conhece qualquer informação
pertinente ao Model, enquanto que, no padrão MVC, a View é obrigada a conhecer o Model
para que ela consiga se atualizar com os dados contidos no Model. No MVP, essas tarefas são
desempenhadas pelo Presenter.
O Model será então responsável por implementar código relacionado ao negócio a ser
modelado, eliminando a necessidade de incluir qualquer código ou dependência relacionada à
componentes externos, como, no caso do MVC, a tarefa de notificação à View quando seu
estado é modificado.
Uma vez baixados os arquivos do servidor, os arquivos JAR contidos nos diretórios
lib e dist devem ser copiados para um diretório que possam ser referenciados pela aplicação
a ser desenvolvida. Caso o desenvolvedor esteja utilizando alguma IDE4 para desenvolver a
aplicação, será necessário adicionar os arquivos descompactados no CLASSPATH do projeto
através de algum assistente fornecido pela própria ferramenta.
O Surf Framework, na versão 0.1 beta, têm como dependência obrigatória apenas o
arquivo commons-logging.jar. Caso seja necessário utilizar o Surf Framework com algum
framework de injeção de dependências – como o Spring [11] ou o Pico [12] – copie também o
arquivo surf-ioc.jar.
Leia, no tópico abaixo, como configurar o Surf Framework em duas das principais
IDEs JavaTM: Eclipse [9] e NetBeans [10].
Para executar a aplicação fora do NetBeans, basta rodar o arquivo run.sh (Linux) ou
run.bat (Windows), situados no diretório raiz da aplicação.
13
2 DESCRIÇÃO DO FUNCIONAMENTO
Outra grande vantagem é que, caso seja necessário deixar de utilizar o Surf
Framework, as Views e Models do sistema não precisam sofrer nenhum processo de
restauração ou manutenção, pois a utilização do Surf Framework não implica em nenhuma
alteração do código JavaTM desses componentes.
2.1.2. Swing
O Surf Framework foi desenvolvido sobre o Swing pelo simples fato de que esta é a
toolkit5 de criação de interfaces com o usuário mais utilizada no mundo. Além disso, existe
muito material de referência e amplo suporte pelas IDEs atuais, tornando a utilização do
Swing mais intuitiva.
5 Toolkit é uma biblioteca ou framework que fornece um conjunto de elementos básicos para a construção de
interfaces com o usuário.
14
2.1.3.2. Conversores
Para que possamos atribuir valores a componentes e objetos de negócio de uma
maneira flexível e prática para o desenvolvedor, o framework possui a capacidade de
converter objetos automaticamente caso o desenvolvedor precise ligar dois objetos de tipos
diferentes. O framework já implementa conversores para todos os tipos primitivos e para os
tipos Date, Calendar e Enum. Caso seja necessário um procedimento customizado para
conversão de dados, pode-se criar conversores de acordo com a necessidade e permitir que
outros tipos de dados possam ser gerenciados automaticamente pelo framework.
2.1.3.3. Formatadores
O framework permite que o desenvolvedor escolha a forma que os dados devem ser
apresentados na View. Podemos, por exemplo, exibir um valor booleano na View de diferentes
maneiras. Podemos também criar formatadores ou utilizar os formatadores existentes na API
JavaTM (como a classe SimpleDateFormat, utilizada para formatar datas).
2.1.5. Binding
15
Para facilitar ainda mais a vida do desenvolvedor, o Surf Framework permite que
componentes da View sejam ligados a propriedades do Model cujos tipos são incompatíveis.
Para citar um exemplo, o desenvolvedor pode querer mostrar a idade de um cliente (objeto
Short) em um componente JTextField (que serve para entrada de texto). Caso o Surf
Framework perceba que as propriedades que estão sendo ligadas sejam incompatíveis - como
o objetos String e Short mostrados no exemplo - os dados são automaticamente convertidos.
Isso facilita bastante a vida do desenvolvedor pois, em um dado momento ele pode
simplesmente trocar o JTextField por um JSpinner para exibir a idade do cliente, sem que
ele precise modificar código nenhum.
2.1.6. Commands
Através dos Commands é que podemos fazer com que a aplicação responda aos
comandos do usuário. A razão da existência dos Commands é tratar uma deficiência dos
Listeners7, do Swing.
Para citar apenas um exemplo que demonstra tal afirmação, vamos supor que nossa
aplicação possua uma classe que implementa a interface ActionListener, cujo código é a
implementação necessária para persistir um objeto Cliente no banco de dados. Então,
podemos utilizar tal classe em um componente JButton para que, quando este for clicado, o
objeto Cliente seja persistido no banco de dados. Mas, vamos supor também que, devido a
uma mudança nos requisitos, o objeto Cliente não deve mais ser persistido quando um
componente é acionado, e sim, quando o usuário fecha a janela de cadastro. Como um objeto
do tipo ActionListener não pode ser utilizado para executar um evento de janela (que
trabalha apenas com objetos do tipo WindowListener), então um novo objeto que
implementa WindowListener deve ser criado com a lógica de persistência para que este
6 Habilidade de se conectar dois objetos de modo que se possa atribuir uma informação a ambos os objetos de
forma homogênea.
7 Os Listeners, ou Ouvintes, são classes que possuem métodos de callback que são executados em pontos bem
definidos durante a interação do usuário com a aplicação, como, por exemplo, ao acionar um botão ou fechar
um formulário.
16
Podemos também fazer com que vários Commands sejam executados quando um
determinado evento é disparado. Isso possibilita que o desenvolvedor divida um código mais
complexo em pequenas partes que podem ser combinadas de diferentes formas, diminuindo o
índice de duplicação de código.
2.1.7. Presenter
O Presenter é a peça chave do padrão MVP e é através do Presenter que o
desenvolvedor consegue usufruir de muitos dos recursos que estão sendo descritos neste
documento. Basicamente, o Presenter possui uma referência ao objeto do formulário (View),
objetos de negócio (Model) e métodos que representam a lógica de apresentação. Os objetos e
informações necessárias para o funcionamento dos Commands e do data dinding, por
exemplo, já estão implementadas e o desenvolvedor não precisa se preocupar em como o
framework trabalha internamente para conseguir utilizá-lo.
Os componentes utilizados para a confecção desta View foram: três JLabel, dois
JTextField, um JSpinner e dois JButton.
Caso o usuário deseje transferir as informações do Model para a View, basta que ele
limpe os campos e pressione o botão “Get”. Ao pressionar o botão, as informações do Model
são atualizadas na View:
18
Note que este é um objeto que nos permite apenas armazenar e manipular as
informações referentes a uma pessoa. Este objeto também não possui nenhuma dependência
em relação ao Surf.
// ...
@Bind
public javax.swing.JSpinner getAge() {
return age;
}
@Bind
public javax.swing.JTextField getEmail() {
return email;
}
@Bind
public javax.swing.JTextField getFullname() {
return fullname;
}
@Interaction
public javax.swing.JButton getSendToModel() { // Botão “Set”
return sendToModel;
}
@Interaction
public javax.swing.JButton getSendToView() { // Botão “Get”
return sendToView;
}
// ...
}
Como pode ser visto na listagem 1, além dos métodos get existem anotações8 nestes
métodos.
@Presenter("personPresenter")
@View(PersonFrame.class)
public class PersonPresenter extends SwingPresenter {
@Model
public Person getPerson() {
return person;
}
Todo Presenter que manipula Views Swing deve estender a classe SwingPresenter.
Além disso, o Presenter deve ser configurado com as anotações @Presenter e @View, que
indicam, respectivamente, o nome que identifica o Presenter e a classe que será utilizada
como View, sendo que esta deve estender de JDialog ou JFrame.
Para que o desenvolvedor possa receber e passar dados para a View, o Presenter deve
ter atributos cujos métodos get são identificados com a anotação @Model. Nesta aplicação o
Model é o atributo person, portanto o método getPerson() deve ser anotado com @Model.
public Main() {
config.addPresenterMapping(new
AnnotationPresenterConfigFactory(config)
.getMapping(PersonPresenter.class));
new SwingPresenterFactory(config)
.createPresenter("personPresenter").showView(true);
}
Finalmente, para que possamos criar o objeto Presenter, precisamos utilizar o método
createPresenter() da classe SwingPresenterFactory. O método createPresenter()
recebe uma String como parâmetro, que indica qual é o Presenter que deve ser criado. Note
que este parâmetro deve ser igual ao valor definido na anotação @Presenter. Finalmente a
22
O código necessário para configurar o Surf manualmente é parecido com o trecho que
segue:
apenas um objeto da classe SwingSurfConfig para toda a aplicação, onde todos os Presenters
são registrados. O mesmo acontece com o objeto da classe SwingPresenterFactory, sendo
que este é o que efetivamente será utilizado para criar instâncias dos Presenters da aplicação.
De modo a manter tais objetos acessíveis, é recomendado a criação de uma classe singleton9
para facilitar o acesso ao objeto SwingPresenterFactory acessível.
Uma vez que o Surf tenha sido devidamente configurado e os Presenters da aplicação
tenham sido registrados, podemos então criar instâncias dos Presenters através do objeto
presenterFactory e torná-los visíveis para que o usuário possa interagir com os mesmos. A
instrução da linha 8 serve para obter um Presenter registrado sob o nome “MainPresenter”.
Então, depois de obtido o Presenter, o exibimos sua View para o usuário através do método
showView().
Para saber quais são os outros métodos fornecidos na interface Presenter, utilize a
documentação Javadoc [13].
9 A intenção do padrão Singleton é garantir que uma classe tenha somente uma instância e fornecer um ponto
global de acesso para a mesma. [GAMMA, 2000]
10 O padrão Injeção de Dependências é um princípio da Orientação a Objetos que inverte a forma com que um
objeto obtém suas dependências, sendo estas externalizadas em outro componente centralizado, de fácil
modificação, de forma que tal objeto não precise satisfazer suas dependências através de instruções ad hoc.
11 DAO significa Data Access Object e é um padrão de projeto cuja função é isolar o código que manipula
diretamente o banco de dados do restante do código da aplicação, permitindo que o desenvolvedor utilize
diferentes bancos de dados apenas substituindo os objetos DAO.
25
O bean presenterSetup é uma classe de integração fornecida pelo Surf para que o
framework possa interceptar mudanças no ciclo de vida dos beans declarados no arquivo de
configuração e, quando os Presenters forem criados pelo Spring, fazer a configuração desses
Presenters.
12 JavaBeans é uma especificação que define algumas diretrizes de codificação de classes de modo que tais
classes possam ser manipuladas e instanciadas em tempo de execução, através da API Reflection.
26
1: BeanFactory beanFactory =
new ClassPathXmlApplicationContext("ApplicationContext.xml");
2: MainPresenter presenter =
(MainPresenter)beanFactory.getBean(“mainPresenter”);
Mais detalhes sobre o Spring não fazem parte do escopo deste documento, podendo
ser obtidos na página do projeto.
Enquanto o Spring faz uso de um arquivo XML para definição das dependências, o
Pico é configurado através de código JavaTM. O Pico é um framework altamente
“embarcável”: possui apenas um JAR com aproximadamente 110KB, enquanto que o Spring
possui em torno de 1.8MB, tornando então o Pico uma excelente opção quando não se deseja
gastar muito espaço com dependências.
Depois de executada a linha 13, já podemos utilizar o Pico para obter instâncias dos
objetos registrados. Isso pode ser feito da seguinte forma:
27
1: MainPresenter p =
(MainPresenter)pico.getComponentInstanceOfType(MainPresenter.class);
Esse código retorna uma instância da classe MainPresenter com todas suas
dependências resolvidas.
Mais detalhes sobre o Pico não fazem parte do escopo deste documento, podendo ser
obtidos na página do projeto.
alguns exemplos, ainda relacionados à ilustração 8, mostrada à pouco. Suponha que o “objeto
de partida” é o objeto Cliente:
Ou seja, essa é uma forma de indicar qual é o “caminho” do atributo dentro do grafo
de objetos. Esse “caminho” são os nomes de propriedades separadas por “.” (ponto).
O Surf implementa uma série de anotações que devem ser utilizadas no código de
modo a tornar a configuração dos Presenters, Views e Models o mais simples quanto possível.
3.3.1.1.1. @Bind
● Sintaxe: @Bind (String model = “”, String field = “”)
● Descrição: Esta anotação é utilizada para ligar uma propriedade de um objeto de
negócio com um componente na View. Por exemplo, podemos relacionar o atributo
nome do objeto cliente com um JTextField que está na View.
● Atributos:
● model: Indica qual é nome do objeto de negócio situado no Presenter (e anotado
com @Model) que se relaciona com o componente anotado. Esse nome nada mais é
que o nome do método get do objeto de negócio anotado com @Model. Se o
componente é relacionado com o Model cliente no Presenter, e, no Presenter
existe um método getCliente(), então devemos setar model da anotação @Bind
com o valor “cliente”.
● field: Informa, dentro do objeto de negócio indicado na propriedade anterior, qual
é a propriedade que efetivamente será relacionada com o componente. Por
29
@Bind
public JTextField getField() {
return field;
}
}
● Observações:
● Caso o Presenter possua apenas um objeto de negócio, a propriedade model pode
ser ignorada e o Surf detectará automaticamente que há apenas um Model sendo
utilizado naquele Presenter.
● A propriedade field também é opcional caso o desenvolvedor opte por programar
por convenções. Se o objeto cliente possui um atributo nome e o componente na
View também chama nome (método getNome()), então o Surf determina que tal
atributo deve ser relacionado com tal componente. Para desabilitar este recurso,
basta utilizar as propriedades model e field da anotação para fazer a configuração
do Model e seu atributo de acordo com o desejado.
3.3.1.1.2. @Formatter
● Sintaxe: @Formatter (Class<? extends Format> type, String pattern = “”)
● Descrição: Esta anotação é utilizada para formatação de valores na View. Podemos
utilizar esta anotação para mostrar datas em formatos específicos, formatar valores
booleanos, mensagens de texto, números ou qualquer outra informação.
● Atributos:
● type: Este parâmetro recebe um objeto qualquer Class que estende Format.
● pattern: Definimos qual será o padrão (ou formato) que deverá ser aplicado ao
valor. Por exemplo, para formatarmos uma data no padrão brasileiro, utilizamos o
pattern “dd/MM/yyyy”, instruindo a classe SimpleDateFormat a formatar a data
seguindo a forma dia/mes/ano.
● Onde ela é usada: A utilização desta anotação é semelhante ao especificado na
anotação @Bind:
30
@Formatter(type=SimpleDateFormat.class, pattern=”dd/MM/yyyy”)
public JTextField getDateField() {
return dateField;
}
}
3.3.1.1.3. @Interaction
● Sintaxe:
@Interaction (String name=””,
Class<? extends Command>[] command = PresenterMethodCallerCommand,
String methodName = “”, String target = “”,
EventType event = ACTION, boolean shortCircuiting = true,
@Property[] properties = {})
● Descrição: Esta anotação é utilizada para que possamos adicionar eventos aos
componentes da View.
● Atributos:
● name: Este atributo só é utilizado caso o desenvolvedor deseje recuperar o objeto
que representa esse evento de dentro de algum Presenter ou Command, de modo a
permitir que o código associado ao evento possa ser executado arbitrariamente.
Para recuperar o evento no Presenter, por exemplo, o seguinte código pode ser
utilizado:
@Interaction(methodName=”editCategory”)
public JButton getEditCategoryButton() {
return editCategoryButton;
}
@Interaction(copyFrom=”editCategoryItem”)
public JMenuItem getEditCategoryItem() {
return editCategoryItem;
}
}
@Interaction(methodName=”editCategory”)
public JButton getEditCategoryButton() {
return editCategoryButton;
}
@Interaction(copyFrom=”editCategoryItem”,
event=EventType.MOUSE_ENTERED)
public JMenuItem getEditCategoryItem() {
return editCategoryItem;
}
}
@Interaction(command=CloseWindowCommand.class)
public JTextField getButton() {
return button;
}
}
● Observações: Apesar de parecer complexa, esta anotação é bastante simples pois seus
valores padrão são suficientes para resolver a maioria das situações.
3.3.1.1.4. @InteractionList
● Sintaxe: @InteractionList (@Interaction[] value)
33
@InteractionList({
@Interaction(command=CloseWindowCommand.class),
@Interaction(command=AnotherCommand.class,
event=EventType.MOUSE_ENTERED)
})
public JTextField getButton() {
return button;
}
}
● Observações: Nenhuma.
3.3.1.1.5. @Property
● Sintaxe: @Property (String name, String value)
● Descrição: Esta é uma anotação de propósito geral, utilizada quando se deseja passar
valores para uma determinada anotação.
● Atributos:
● name: Qual deve ser o nome da propriedade. Deve se ter cuidado ao definir o
nome, pois ele deve ser único no contexto ao qual está inserido, de modo a
identificar a propriedade;
● value: Valor que deve ser passado.
● Onde ela é usada: Essa anotação é usada atualmente como um atributo da anotação
@Interaction, podendo ser reaproveitada futuramente para outros propósitos.
Abaixo, um trecho de código que mostra a anotação em uso:
34
@Interaction(
command=CloseWindowCommand.class,
properties={
@Property(name=”propName”, value=”propValue”),
@Property(name=”propName2”, value=”propValue2”)
}
)
public JTextField getButton() {
return button;
}
}
return CONTINUE;
}
}
● Observações: Nenhuma.
3.3.1.1.6. @SubView
● Sintaxe: @SubView (String model = “”)
● Descrição: Um outro recurso muito interessante do Surf é que ele permite que a View
seja composta tanto de componentes simples – como um JTextField – como de
componentes compostos – como JPanel. Então, o desenvolvedor pode simplificar o
35
Para criar uma subview, crie uma classe que estenda de JPanel. Esta classe, assim
como as outras, não precisa implementar nenhuma interface especial para que esta
possa ser utilizada como subview. Depois de criar o painel e adicionar os componentes
desejados nele, já podemos utilizá-lo da mesma forma que um componente comum na
nossa View:
@Subview(model=”model”)
public MyPanel getMyPanel() {
return myPanel;
}
}
@Bind
public JTextField getField1() {
return field1;
}
@Bind
public JTextField getField2() {
return field2;
}
}
● Atributos:
● model: Devemos indicar qual Model é representado pela subview. Este atributo
funciona de maneira análoga ao atributo model da anotação @Bind.
● Onde ela é usada: Ela é utilizada na View, no método get de algum componente que
estende JPanel.
● Observações: As regras do parâmetro Model são as mesmas do parâmetro Model da
anotação @Bind. É possível utilizar as anotações @Interaction e @InteractionList
em conjunto com a anotação @SubView, tornando possível a definição dos eventos
relacionados aos componentes internos ao painel:
36
@Subview
@Interaction(target=”button”, command=DoSomethingCommand.class)
public MyPanel getMyPanel() {
return myPanel;
}
}
@Bind
public JTextField getField1() {
return field1;
}
@Bind
public JTextField getField2() {
return field2;
}
@Widget
public JButton getButton() {
return button;
}
}
3.3.1.1.7. @Type
● Sintaxe: @Type (Class value)
● Descrição: Esta anotação serve para indicar qual é o tipo que um determinado
componente da View representa. Existe, dentro do Surf, uma mapeamento que indica
quais são os tipos de dados “padrão” para cada componente Swing. Mas existem
componentes que são genéricos o bastante para que o Surf possa atribuir um tipo que
funcione na maioria dos casos. Por exemplo, o componente JComboBox, que pode
conter objetos do tipo String, Cliente ou Produto. Então, para que o Surf saiba
exatamente o tipo de objeto que será armazenado em tal componente, esta anotação é
utilizada.
● Atributos:
● value: Classe que indica o tipo representado pelo componente anotado.
● Onde ela é usada: Esta anotação é utilizada em conjunto com as anotações @Bind ou
@Widget, da seguinte forma:
37
@Bind @Type(Customer.class)
public JComboBox getField() {
return field;
}
}
● Observações: Nenhuma.
3.3.1.1.8. @Widget
● Sintaxe: @Widget ()
● Descrição: Quando um componente é anotado com @Bind, o Surf já o reconhece no
contexto de execução da aplicação. Porém, quando temos um componente que não
deve ser ligado a nenhum Model (binding), mas deve ser utilizado para um propósito
qualquer – como mostrar um valor ou tratar gestos do usuário (através de eventos) –
então, este componente pode ser anotado com @Widget.
● Atributos: Nenhum.
● Onde ela é usada: É usada de forma semelhante à anotação @Bind.
● Observações: A utilização desta anotação pode ser omitida quando um determinado
componente já está anotado com @Bind, @Interaction ou @InteractionList, pois,
ao utilizar tais anotações, o Surf já adiciona tal componente nas configurações,
permitindo que a aplicação manipule este componente através das classes e métodos
fornecidos pelo Surf. A anotação @Widget costuma ser utilizada com mais freqüência
em SubViews, para permitir que os componentes internos à SubView possam receber
configurações de acordo com a View em que tal SubView é utilizada. Um exemplo
sobre como isso é feito pode ser conferido nas listagens relacionadas à anotação
@SubView.
3.3.1.2.1. @Model
● Sintaxe: @Model ()
● Descrição: Esta anotação indica se um determinado atributo do Presenter é um
Model, permitindo que este atributo seja configurado para receber valores dos
componentes da View e mandar valores para os componentes da View, de forma semi-
automática. Mais detalhes serão explicados no capítulo sobre data binding.
● Atributos: Nenhum.
● Onde ela é usada: Dentro da classe Presenter, é necessário declarar atributos que
serão usados como Model. Então, para que o desenvolvedor possa indicar um atributo
38
como sendo um Model, o método get desse atributo deve ser anotado com @Model:
@Presenter(“presenterID”)
public class MyPresenter extends SwingPresenter {
@Model
public Customer getCustomer() {
return customer;
}
● Observações: Todo objeto de negócio que precisa ser utilizado como Model deve
possuir um método get e um método set, além da anotação @Model, que deve estar
localizada no método get. Um outro ponto importante é que, caso o atributo anotado
com @Model não seja inicializado explicitamente pelo desenvolvedor no construtor do
Presenter, o Surf cria uma nova instância do objeto (chamando o construtor padrão
sem argumentos) e o injeta através do método set correspondente. Caso o atributo
anotado seja uma coleção (tipos Collection, List, Map ou Set), então um objeto do
tipo apropriado à interface é criado e injetado através do método set:
● atributo tipo Collection ou List = injeta uma instância de ArrayList;
● atributo tipo Map = injeta uma instância de HashMap;
● atributo tipo Set = injeta uma instância de HashSet.
3.3.1.2.2. @OnViewActivate
● Sintaxe: @OnViewActivate()
● Descrição: Esta anotação serve para executar um determinado método quando a View
é ativada.
● Atributos: Nenhum.
● Onde ela é usada: Ela é usada em um método qualquer do Presenter que se deseja
executar quando a View é ativada. O método deve ser público e não receber nenhum
parâmetro:
@OnViewActivate
public void doOnActivate() {
// Este método será executado quando a View for ativada
}
}
39
● Observações: Nenhuma.
3.3.1.2.3. @OnViewClose
● Sintaxe: @OnViewClose()
● Descrição: Esta anotação serve para executar um determinado método quando a View
tenta ser fechada pelo usuário.
● Atributos: Nenhum.
● Onde ela é usada: Ela é usada em um método qualquer do Presenter que se deseja
executar quando a View é ativada. O método deve ser público, não receber nenhum
parâmetro e pode retornar um boolean, que indica se a View deve ser fechada (true)
ou se o fechamento deve ser interrompido (false). No exemplo abaixo, toda vez que
o usuário tentar fechar a View, o método doOnClose() será executado:
1: @Presenter("mainPressenter")
2: @View(SubscriptionFrame.class)
3: public class MainPresenterImpl extends SwingPresenter
implements MainPresenter {
4:
5: @OnViewClose
6: public boolean doOnClose() { return askForExit(); }
7: }
3.3.1.2.4. @OnViewClosed
● Sintaxe: @OnViewClosed()
● Descrição: Esta anotação serve para executar um determinado método quando a View
for fechada pelo usuário.
● Atributos: Nenhum.
● Onde ela é usada: Ela é usada em um método qualquer do Presenter que se deseja
executar quando a View é fechada pelo usuário. O método anotado deve ser público e
não receber nenhum argumento.
● Observações: Nenhuma.
3.3.1.2.5. @OnViewDeactivate
● Sintaxe: @OnViewDeactivate()
● Descrição: Esta anotação serve para executar um determinado método quando a View
40
3.3.1.2.6. @OnViewDeiconify
● Sintaxe: @OnViewDeiconify()
● Descrição: Esta anotação serve para executar um determinado método quando a View
for restaurada, ou seja, quando o estado da View passa de minimizada para
restaurada ou maximizada.
● Atributos: Nenhum.
● Onde ela é usada: Ela é usada em um método qualquer do Presenter que se deseja
executar quando a View for restaurada. O método anotado deve ser público e não
receber nenhum argumento.
● Observações: Nenhuma.
3.3.1.2.7. @OnViewGainFocus
● Sintaxe: @OnViewGainFocus()
● Descrição: Esta anotação serve para executar um determinado método quando a View
ganhar foco.
● Atributos: Nenhum.
● Onde ela é usada: Ela é usada em um método qualquer do Presenter que se deseja
executar quando a View ganhar foco. O método anotado deve ser público e não
receber nenhum argumento.
● Observações: Nenhuma.
3.3.1.2.8. @OnViewIconify
● Sintaxe: @OnViewIconify()
● Descrição: Esta anotação serve para executar um determinado método quando a View
for minizada.
● Atributos: Nenhum.
● Onde ela é usada: Ela é usada em um método qualquer do Presenter que se deseja
executar quando a View for minimizada. O método anotado deve ser público e não
receber nenhum argumento.
● Observações: Nenhuma.
3.3.1.2.9. @OnViewLostFocus
● Sintaxe: @OnViewLostFocus()
41
● Descrição: Esta anotação serve para executar um determinado método quando a View
perder o foco.
● Atributos: Nenhum.
● Onde ela é usada: Ela é usada em um método qualquer do Presenter que se deseja
executar quando a View perder o foco. O método anotado deve ser público e não
receber nenhum argumento.
● Observações: Nenhuma.
3.3.1.2.10. @OnViewOpened
● Sintaxe: @OnViewOpened()
● Descrição: Esta anotação serve para executar um determinado método quando a View
é exibida pela primeira vez.
● Atributos: Nenhum.
● Onde ela é usada: Ela é usada em um método qualquer do Presenter que se deseja
executar quando a View exibida pela primeira vez. O método anotado deve ser público
e não receber nenhum argumento.
● Observações: Nenhuma.
3.3.1.2.11. @Presenter
● Sintaxe: @Presenter (String value)
● Descrição: Esta anotação serve para associar um Presenter a um identificador.
● Atributos:
● value: Uma String que serve para identificar o Presenter. Essa String deve ser
única, ou seja, não deve haver dois ou mais Presenters configurados sob o mesmo
identificador. Caso isto ocorra, uma exceção em tempo de execução é lançada.
● Onde ela é usada: Na definição de uma classe que estende direta ou indiretamente de
Presenter:
@Presenter(“presenterID”) @View(MyView.class)
public class MyPresenter extends SwingPresenter {
// ...
● Observações: Para que uma classe possa ser anotada com a anotação @Presenter, ela
deve estender direta ou indiretamente de Presenter. Por exemplo, um Presenter que
manipula Views Swing, deve estender a classe SwingPresenter.
3.3.1.2.12. @View
42
A única exigência é que sejam criados métodos get para todos os componentes que se
deseja utilizar recursos do Surf. Apesar de isso parecer ruim, é algo que se resolve com um
par de cliques nas IDEs JavaTM mais modernas. São através desses métodos get que o Surf
consegue acessar os componentes. Não é necessário implementar métodos set para os
componentes.
14 Abreviação de Plain Old Java Object, que serve para classificar classes java que não estendem nenhuma
classe ou interface, ou seja, objetos simples como componentes.
43
1: @Presenter(“nome_presenter”)
2: @View(View.class)
3: public class Presenter extends SwingPresenter {
4: // ...
5: }
Na primeira linha, definimos um nome para este Presenter dentro do Surf. Na segunda
linha, indicamos qual é a classe que será a View (um JFrame ou um JDialog). Na terceira
linha criamos a classe em si.
1: @Presenter(“nome_presenter”)
2: @View(View.class)
3: public class Presenter extends SwingPresenter {
4:
5: private Cliente cliente;
6:
7: @Model
8: public Cliente getCliente() {
9: return cliente;
10: }
11:
12: public void setCliente(Cliente cliente) {
13: this.cliente = cliente;
14: }
15: }
Para definir um atributo como sendo um Model, bastou utilizar a anotação @Model no
método get do atributo desejado. Se não existir nenhuma propriedade no Presenter que deva
ser sincronizada com a View, então não há a necessidade de se utilizar a anotação @Model em
nenhum dos métodos get do Presenter.
indicar qual é o tipo do Presenter relacionado a este Command. Então, não é necessário fazer
casting para obter os dados relativos ao Presenter:
Este recurso deve ser usado com cautela pois, ao utilizar Gennerics em um Command,
se está “amarrando” um Command a um determinado Presenter, tornando impossível a
utilização deste Command com outros Presenters que não sejam o especificado. Portanto, os
comportamentos dos Presenters devem ser modelados como interfaces de modo que os
Commands sejam “amarrados” à comportamentos, e não a implementações.
Por exemplo, vamos analisar o seguinte trecho de código que pertence a uma View
qualquer:
// ...
@Interaction(methodName=”executar”)
public Jbutton getButton() {
return button;
}
classes e métodos onde os tipos de objetos tratados por eles são definidos na hora em que são invocados. Este
recurso foi adicionado na versão 5.0 (Tiger) do JavaTM.
46
@Presenter(“MyPresenter”) @View(MyView.class)
public class MyPresenter extends SwingPresenter {
// ...
Listagem 30: Método que será executado pelo Surf ao disparar o evento
Existe uma outra forma de se fazer isso. O desenvolvedor pode omitir o parâmetro
methodName, na anotação @Interaction e, quando o evento for disparado, um método de
mesmo nome ao método anotado com @Interaction (sem o 'get'), será executado no
Presenter:
// ...
@Interaction
public Jbutton getCadastrarUsuario() {
return button;
}
@Presenter(“MyPresenter”) @View(MyView.class)
public class MyPresenter extends SwingPresenter {
// ...
a execução de um evento, podendo este ser composto por vários Commands. A instância
desse objeto de contexto é criada e passada a todos os Commands antes que o evento comece
a ser efetivamente processado.
@Interaction(command={ValidateCommand.class, ExecuteCommand.class})
public JButton getButton() {
return button;
}
}
getCommandContext().setParameter(“valid”, valid);
return CONTINUE;
}
}
boolean valid =
((Boolean)getCommandContext().getParameter("valid")).booleanValue(
);
if (valid) {
// Faz qualquer coisa...
}
else {
// Faz outra coisa...
}
return CONTINUE;
}
}
Então, de modo a tornar essa sincronia mais simples, o Surf fornece um módulo de
data binding que transporta e converte os dados automaticamente, através da invocação de
49
Caso não seja necessário sincronizar todos os atributos, podemos também fazer a
sincronização de atributos individuais ou de uma lista de atributos. Por exemplo:
1: presenter.getDataTransferer().updateModel(null, “nome”);
2: presenter.getDataTransferer().updateView(new String[] {“nome”,
“endereco”});
3:
4: List<String> attrs = new ArrayList<String>();
5: attrs.add(“nome”);
6: attrs.add(“endereco”);
7: presenter.getDataTransferer().updateView(attrs);
Na linha 7, o programa utiliza um objeto List contendo todos os atributos que devem
ser sincronizados e o passa para o método updateView(), fazendo com que tais propriedades
da View sejam atualizadas. O resultado obtido nesta instrução é idêntico ao obtido na
50
instrução da linha 2.
1: presenter.getDataTransferer().updateModel(“cliente”, “nome”);
Listagem 38: Indicando o nome do Model que deve ser utilizado na sincronia
A diferença entre informar ou omitir o nome do Model só pode ser notada caso
existam atributos com nomes iguais para diferentes Models. Por exemplo: suponha que você
tenha dois Models declarados no seu Presenter: cliente e fornecedor. Ambos os Models
possuem um atributo nome. Então, quando se desejar sincronizar a propriedade nome do
cliente, devemos informar a String “cliente” no parâmetro que representa o nome do Model
que deve ser usado. Caso se deseja trabalhar com o atributo nome do fornecedor, então
devemos fornecer a String “fornecedor” no parâmetro que representa o nome do Model que
deve ser usado. Ou, ainda, caso se deseja sincronizar o atributo nome, tanto de cliente quanto
de fornecedor, basta informar null no lugar do nome do Model. Assim, todos os atributos
nome encontrados serão sincronizados, independente do Model em que estão declarados.
O recurso de nested properties pode ser utilizado no nome do atributo que se deseja
sincronizar. Por exemplo, podemos atualizar o atributo rua que está situado dentro do atributo
endereco:
1: presenter.getDataTransferer().updateModel(null, “endereco.rua”);
// Gets e Sets
// ...
}
Caso o componente represente um valor do tipo inteiro, então, quando o valor desse
componente for atualizado com o valor da Enum, o valor setado nesse componente é o índice
do valor enumerado. Por exemplo, se o Enum em questão fosse o enumerador Sexo,
mostrado a pouco, e o valor da propriedade sexo do objeto Pessoa fosse FEMININO, então o
valor assumido pelo componente da View seria o inteiro 1, que corresponde ao índice do
elemento FEMININO na Enum Sexo.
Caso o componente represente um valor do tipo String, então, quando o valor desse
componente for atualizado com o valor da Enum, o valor setado nesse componente é o nome
do valor numerado. Por exemplo, se o Enum em questão fosse o enumerador Sexo, mostrado
a pouco, e o valor da propriedade sexo do objeto Pessoa fosse FEMININO, então o valor
assumido pelo componente da View seria a String “FEMININO”, que corresponde ao nome
do elemento da Enum Sexo.
1: presenter.getDataTransferer().getViewProperty("propriedade");
Essa abstração permite que trabalhemos com diferentes tipos de componentes sem a
preocupação de qual componente se trata, permitindo a troca desse componente futuramente,
sem precisar alterar o código que o manipula. Uma observação importante: podemos
representar propriedades aninhadas no parâmetro deste método, permitindo que seja possível
obter o valor de componentes contidos dentro de subviews.
52
1: presenter.getWidget("property.subproperty").getComponent();
Este método retorna uma referência a um componente Swing, caso o componente que
está sendo indicado no parâmetro for um JTextField, por exemplo. Apesar de não ser
aconselhável o uso do método getComponent(), ele se torna necessário em determinados
momentos, quando é preciso fazer alguma manipulação mais “baixo nível”.
1: // Habilita
2: presenter.getWidget(“property.subproperty”).setEnabled(true);
3:
4: // Desabilita
5: presenter.getWidget(“property.subproperty”).setEnabled(false);
A seguir será mostrado, em detalhes, como trabalhar com componentes menos triviais.
uma subclasse desta, customizada pelo desenvolvedor) e adicionar no JList como sendo um
Model:
switch (columnIndex) {
case 0: return new Byte(task.getPriority());
case 1: return task.getDescription();
case 2: return task.getDueDate() != null
? dateFormat.format(task.getDueDate().getTime()) : "";
case 3: return task.getFinishTime() != null
? dateFormat.format(task.getFinishTime().getTime()) : "";
case 4: return new Boolean(task.isAlert());
}
return null;
}
switch (columnIndex) {
case 0:
entity.setPriority((Byte)newValue);
break;
case 1:
entity.setDescription((String)newValue);
break;
case 4:
entity.setAlert(((Boolean)newValue).booleanValue());
break;
}
sendo renderizado pela tabela via parâmetro, ficando mais fácil de obtermos os dados para
exibição na tabela. Algo semelhante ocorre no método setValue(), onde o objeto sendo
modificado também é passado no parâmetro, simplificando a criação de tabelas editáveis.
@Presenter(“MyPresenter”) @View(MyView.class)
public class MyPresenter extends SwingPresenter {
// ...
// ...
}
// ...
@Bind(field=”sexo”)
public ButtonGroup getGroupSexo() {
return groupSexo;
}
// ...
Cada componente possui um tipo diferente de seleção, que varia de acordo com a
forma com que o componente pode ser utilizado. Por exemplo, a seleção de um JTable pode
ser uma lista de objetos Cliente – que correspondem às linhas selecionadas – enquanto que a
seleção de um JTextField é uma String – que corresponde ao texto selecionado.
@Presenter(“MainPresenter”) @View(MainWindow.class)
public class MainPresenter extends SwingPresenter {
// ...
}
@Presenter("SomePresenter") @View(SomwView.class)
public class SomePresenter extends SwingPresenter {
Esta aplicação serve para mostrar alguns recursos mais avançados que não foram
mostrados na primeira aplicação de exemplo, além de mostrar a integração do Surf
Framework com outros frameworks (como o Hibernate e o Pico). Com isso, a idéia é mostrar
que o Surf Framework pode ser aplicado em softwares mais complexos.
Para permitir a inclusão de uma nova tarefa e a manutenção de uma tarefa previamente
cadastrada, o usuário utiliza a seguinte janela:
59
O restante deste documento irá detalhar a construção deste aplicativo, cujo objetivo
principal é demonstrar como o Surf Framework pode ajudar a agilizar o desenvolvimento de
aplicações Swing mais modulares e fáceis de manter.
Como foi dito anteriormente, para que o Surf Framework possa interagir com o Model,
não é necessária nenhuma modificação no mesmo. Esta é a primeira grande vantagem em se
utilizar o Surf Framework.
60
O código do Model é bem simples e não será necessário listá-lo neste documento, pois
trata-se de uma classe que somente armazena dados e gerencia o acesso a eles através de
métodos acessores (get's e set's).
void saveTask();
void removeTask();
void refreshTasks();
}
void clearTasks();
void showCompletedTasks();
void showAlertTasks();
}
Para completar as interfaces listadas acima, devemos criar as interfaces dos Presenters
propriamente ditos, que agrupam, além dos métodos de negócio, métodos responsáveis por
gerenciar o estados das respectivas Views.
61
void enableInterfaceButtons();
@Presenter("MainPresenter") @View(MainWindow.class)
public class MainPresenterImpl extends SwingPresenter
implements MainPresenter {
@Model
public List<Task> getTasks() {
return tasks;
}
Para definir uma classe como sendo um Presenter capaz de gerenciar Views Swing,
essa classe deve estender SwingPresenter.
A seguir estão dispostos trechos de código que mostram algumas das principais
funções desempenhadas pela janela principal. No método que segue, capturamos quais são as
tarefas selecionadas na JTable e modificamos o estado dos componentes da janela de acordo
com a seleção, habilitando e desabilitando tais componentes:
Collection<Task> c =
getWidget("taskTable").getSelection().getSelected();
getWidget("editTaskButton").setEnabled(hasSelection);
getWidget("editTaskItem").setEnabled(hasSelection);
getWidget("removeTasksButton").setEnabled(hasSelection);
getWidget("removeTasksItem").setEnabled(hasSelection);
getWidget("markAsCompletedButton").setEnabled(hasSelection);
getWidget("markAsCompletedItem").setEnabled(hasSelection);
if (hasSelection) {
selectedTask = c.iterator().next();
getWidget("markAsCompletedButton")
.setValue(selectedTask.isCompleted());
getWidget("markAsCompletedItem")
.setValue(selectedTask.isCompleted());
}
}
O código que segue é responsável por mostrar as tarefas na grid. Ele verifica quais
foram os filtros selecionados pelo usuário e traz as tarefas correspondentes:
63
tasks = taskManager.getAllTasks();
if (getWidget("showAlertsItem").getValue().equals(true)) {
tasks = taskManager.getAlertedTasks(tasks);
}
if (getWidget("showCompletedItem").getValue().equals(true)) {
tasks = taskManager.getCompletedTasks(tasks);
}
if (getWidget("sortByDueDateItem").getValue().equals(true)) {
taskManager.sortTasksByDueDate(tasks);
}
if (getWidget("sortByFinishTimeItem")
.getValue().equals(true)) {
taskManager.sortTasksByFinishDate(tasks);
}
if (getWidget("sortByPriorityItem")
.getValue().equals(true)) {
taskManager.sortTasksByPriority(tasks);
}
getDataTransferer().updateView();
}
O código que segue mostra a implementação dos métodos de negócio, que utilizam a
classe TaskManager para fazer a integração do sistema com o banco de dados:
Isso mostra como a integração do Surf Framework com as soluções ORM16 existentes
16 Object Relational Mapping, ou Mapeamento Objeto-Relacional. São soluções criadas com o objetivo de
fazer com que linguagens orientadas a objetos possam interagir com bancos de dados relacionais.
64
no mercado ocorre facilmente. Nesta aplicação foi utilizado o EJB 3.0, implementado pelo
Hibernate, para persistência dos dados.
@Presenter("TaskDetailsPresenter")
@View(TaskDetailsDialog.class)
public class TaskDetailsPresenterImpl extends SwingPresenter
implements TaskDetailsPresenter {
// ...
getWidget("daysBefore").setEnabled(b);
getWidget("daysBeforeLabel").setEnabled(b);
}
if ("".equals(task.getDescription())) {
statusLabel.setValue(
"The field 'Description' is mandatory."
);
ret = false;
}
else if (task.getDueDate() == null) {
statusLabel.setValue(
"The field 'Due Date' is mandatory."
);
ret = false;
}
if (ret == false) {
statusLabel.setIcon(
new ImageIcon(getClass().getResource(
"/icons/16x16/dialog-error.png"
)));
}
return ret;
}
Depois de analisar alguns trechos de código da listagem 60 podemos ver outra grande
vantagem do Surf Framework: o Presenter não é dependente dos componentes utilizados na
View. Dessa forma, caso seja necessário trocar alguns componentes da View, o Presenter não
precisa ser modificado.
Ainda na listagem 61, um outro ponto que merece uma explicação é a instrução
situada na linha 11. O método execute() retorna um objeto String qualquer. Esta String é
utilizada pelo Surf Framework para indicar se o Command foi executado com sucesso ou não.
Para efeitos de padronização, o Surf Framework já define algumas constantes indicativas de
status, sendo as mais importantes as constantes CONTINUE e STOP.
Quando um evento é disparado, podemos fazer com que vários Commands sejam
executados. Então, Surf Framework executa os Commands um a um, na mesma ordem em
que são declarados. Caso um Command retorne CONTINUE, o próximo Command é
executado. Mas, caso um Command retorne STOP, a execução dos Commands subseqüentes
é interrompida.
Existe ainda uma outra forma de responder a eventos do usuário, sendo esta bem
simples e prática, bastante útil quando se deseja tratar um evento mais simples.
@Interaction
public javax.swing.JMenuItem getShowAboutDialog() {
return aboutItem;
}
O método get do componente aboutItem está anotado com @Interaction, sendo que
a anotação não possui nenhum parâmetro. Isso indica que, quando o evento de ação for
disparado no componente aboutItem, o Surf irá invocar um método chamado
showAboutDialog(), sendo que tal método está situado dentro do Presenter. A listagem
abaixo mostra como fica esse método de forma que, quando o usuário clicar nesse item de
67
Depois de desenhada a janela, devemos criar métodos get para cada componente que
queremos disponibilizar para o Surf Framework. Depois de criados os métodos, devemos
utilizar as anotações nesses métodos get de modo a ligar os controles da janela com o Model e
prover a funcionalidade da aplicação através da instalação de Commands nos componentes.
O trecho de código abaixo mostra como deve ser feito para adicionar comportamento a
um componente visual, além de permitir que um componente seja sincronizado com o Model:
68
1: @Interaction(methodName=”addNewTask”)
2: public javax.swing.JButton getAddTaskButton() {
3: return addTaskButton;
4: }
5:
6: @Interaction(methodName=”editTask”)
7: public javax.swing.JButton getEditTaskButton() {
8: return editTaskButton;
9: }
10:
11: @Interaction(command={
12: MarkAsCompletedCommand.class,
13: SaveTaskCommand.class
14: })
15: public javax.swing.JToggleButton getMarkAsCompletedButton() {
16: return markAsCompletedButton;
17: }
18:
19: @Interaction(command={
20: ShowAlertsCommand.class,
21: ListTasksCommand.class
22: })
23: public javax.swing.JToggleButton getShowAlertsButton() {
24: return showAlertsButton;
25: }
26:
27: @Interaction(command={
28: ShowCompletedTasksCommand.class,
29: ListTasksCommand.class
30: })
31: public javax.swing.JToggleButton getShowCompletedButton() {
32: return showCompletedButton;
33: }
34:
35: @Bind(model="tasks")
36: @InteractionList({
37: @Interaction(methodName="taskSelectionChange",
38: event=EventType.LIST_SELECTION_CHANGE),
39: @Interaction(copyFrom="editTaskItem",
40: event=EventType.MOUSE_DOUBLE_CLICKED)
41: })
42: public javax.swing.JTable getTaskTable() {
43: return taskTable;
44: }
Ainda sobre listagem 64, linha 35, para que possamos ligar um componente visual a
69
um objeto de negócio, utilizamos a anotação @Bind. Nela, definimos qual é nome do Model
correspondente e qual é o nome da sub-propriedade do Model, caso exista. Também podemos
adicionar uma anotação @Interaction neste componente para fazê-lo responder aos eventos
disparados pelo usuário.
Caso o Presenter possua apenas uma propriedade anotada com @Model, não
precisamos fornecer o nome do Model na anotação @Bind.
1: @Interaction(command=CloseDialogCommand.class)
2: public javax.swing.JButton getCancelButton() {
3: return cancelButton;
4: }
5:
6: @Interaction(command={
7: ValidateTaskCommand.class,
8: SaveTaskCommand.class,
9: CloseDialogCommand.class,
10: ListTasksCommand.class
11: })
12: public javax.swing.JButton getSaveButton() {
13: return saveButton;
14: }
15:
16: @Bind
17: public javax.swing.JCheckBox getCompleted() {
18: return completed;
19: }
20:
21: @Bind
22: public javax.swing.JSpinner getDaysBefore() {
23: return daysBefore;
24: }
25:
26: @Bind @Formatter(type=SimpleDateFormat.class)
27: public javax.swing.JFormattedTextField getDueDate() {
28: return dueDate;
29: }
O software de demonstração utiliza o framework Pico, por este ser mais simples e
rápido em relação ao Spring. Mas, para fins de demonstração, também será mostrado neste
documento como ficaria a integração do Surf Framework com o Spring.
<beans>
<bean id="taskManager"
class="br.com.surf.todomanager.model.manager.DaoTaskManager">
<property name="taskDao">
<bean
class="br.com.surf.todomanager.dao.task.HSQLDBTaskDao" />
</property>
</bean>
<bean id="mainPresenter"
class="br.com.surf.todomanager.presenter.MainPresenterImpl">
<property name="taskManager" ref="taskManager" />
</bean>
<bean id="taskDetailsPresenter"
class="br.com.surf.todomanager.presenter.TaskDetailsPresenterImpl">
<property name="taskManager" ref="taskManager" />
<property name="parentPresenter" ref="mainPresenter" />
</bean>
<bean id="presenterSetup"
class="br.com.surf.integration.spring.SwingSpringPresenterSetup">
<property name="configFactory"
value="br.com.surf.annotation.parser.AnnotationPresenterConfigFactor
y" />
<property name="presenterList">
<list>
<ref bean="mainPresenter" />
<ref bean="taskDetailsPresenter" />
</list>
</property>
</bean>
</beans>
Para obter um bean criado pelo Spring, foram criadas as seguintes classes:
MainPresenter getMainPresenter();
TaskDetailsPresenter getTaskDetailsPresenter();
AboutPresenter getAboutPresenter();
}
static {
/* Padrão de projeto Singleton */
_instance = new SpringPresenterManager();
}
private SpringPresenterManager() {
static {
_instance = new PicoPresenterManager();
}
private PicoPresenterManager() {
/* Dependências da aplicação */
pico.registerComponentImplementation(
TaskDetailsPresenterImpl.class);
pico.registerComponentImplementation(MainPresenterImpl.class);
pico.registerComponentImplementation(AboutPresenterImpl.class);
/* Camada de persistência */
pico.registerComponentImplementation(HSQLDBTaskDao.class);
pico.registerComponentImplementation(DaoTaskManager.class);
pico.start();
}
Diferentemente do Spring, o Pico não exige nenhum arquivo de configuração para que
este funcione. Durante a invocação do método start(), o Pico analiza os construtores dos
objetos registrados anteriormente através do método registerComponentImplementation()
e, automaticamente, cria os objetos na ordem correta, passando as referências dos objetos
através de seus construtores.
Então, para que possamos obter uma referência a algum Presenter, basta utilizarmos
os métodos definidos nas classes SpringPresenterManager ou PicoPresenterManager.
public Main() {
O código desta classe apenas obtém uma referência para o Presenter principal da
aplicação já criado pelo Pico e solicita a exibição de sua View. O parâmetro boolean indica se
a View deve ser exibida (true) ou escondida (false).
75
5 CONSIDERAÇÕES FINAIS
O objetivo deste trabalho foi mostrar, de uma forma breve, dois padrões de projeto que
podem ser aplicados para tornar o desenvolvimento de aplicações Swing mais simples, dando
uma atenção especial em como tais padrões ajudam a tornar o código das aplicações melhor
estruturados e organizados.
6 TRABALHOS FUTUROS
Existem duas coisas extremamente importantes e que precisam ser feitas com uma
certa urgência:
Os testes não foram escritos para o Surf Framework devido ao pouco tempo disponível
para o desenvolvimento do trabalho, em razão das funcionalidades planejadas para o
framework.
Uma outra questão que poderá ser trabalhada no futuro é a possibilidade da utilização
do Surf Framework com outras toolkits de desenvolvimento de interfaces visuais, como SWT
[1] e Thinlets [2], além de melhorar o suporte ao Swing. O Surf Framework foi
cuidadosamente projetado para que tais customizações sejam fáceis de serem incorporadas ao
framework.
7 REFERÊNCIAS BIBLIOGRÁFICAS
[WINK, 2006] WINK, D. V.; JUNIOR, V. G. AspectJ: Programação Orientada a Aspectos
com Java. São Paulo: Novatec, 2006. 229 p.
BAUER, C.; KING, G. Hibernate em Ação: O guia definitivo para o Hibernate. Rio de
Janeiro: Ciência Moderna, 2005. 529 p.
WALLS, C.; BREIDENBACH, R. Spring em Ação. Rio de Janeiro: Ciência Moderna, 2006.
445 p.
TATE, B. A.; GEHTLAND, J. Better, Faster, Lighter Java. O'Reilly, 2004. 243 p.
FREEMAN, E.; FREEMAN, E. Use a Cabeça! Padrões de Projetos. Rio de Janeiro: Alta
Books, 2005. 496 p.
FOWLER, M. Model-View-Presenter.
Disponível em: <http://www.martinfowler.com/eaaDev/ModelViewPresenter.html>. Acesso
em: 28 fev. 2006.
MILLER, J. D. Test Driven Development with ASP.Net and the Model View Presenter
Pattern.
Disponível em:
<http://codebetter.com/blogs/jeremy.miller/archive/2006/02/01/137457.aspx>. Acesso em: 25
mar. 2006.
Model-View-Presenter.
Disponível em: <http://c2.com/cgi/wiki?ModelViewPresenter>. Acesso em: 25 mar. 2006.
Model-View-Presenter Framework.
Disponível em:
<http://www.mimuw.edu.pl/~sl/teaching/00_01/Delfin_EC/Overviews/ModelViewPresenter.
htm>. Acesso em: 24 mar. 2006.
Pattern: Model-View-Presenter.
Disponível em:
<http://www.mimuw.edu.pl/~sl/teaching/00_01/Delfin_EC/Patterns/MVP.htm>. Acesso em:
28 fev. 2006.
[1] SWT.
79
[2] Thinlets.
Disponível em: <http://thinlet.sourceforge.net/home.html>. Acesso em: 10 dez. 2006.
[3] Groovy.
Disponível em: <http://groovy.codehaus.org>. Acesso em: 10 dez. 2006.
[5] Commons-Validator.
Disponível em: <http://jakarta.apache.org/commons/validator>. Acesso em: 10 dez. 2006.
[8] Commons-Logging.
Disponível em: <http://jakarta.apache.org/commons/logging>. Acesso em: 21 mar. 2006.
[9] Eclipse.
Disponível em: <http://www.eclipse.org>. Acesso em: 20 mar. 2006.
[10] NetBeans.
Disponível em: <http://www.netbeans.org>. Acesso em: 20 mar. 2006.