Desenvolvimento Orientado a
Componentes e Reuso
Construção de Componentes
JavaBeans
Prof. Thiago Affonso de M. N. Viana
[email protected]
JavaBeans
• Em Java, Bean significa Componente
• Tem dois modelos de componentes em Java:
– JavaBeans normais para componentes
baseados em eventos
• Frequentemente (mas não necessariamente)
feitos para GUI
– Enterprise JavaBeans (EJB) para Server
Components
• Não há relação entre os dois modelos
O que é um JavaBean?
•
•
•
•
Portátil
Independente de plataforma
Orientado a componentes
Linguagem Java.
Introdução
• Foi bolado para permitir que componentes
reutilizáveis pudessem ser compostos em
outros JavaBeans, applets e componentes
usando ferramentas visuais.
• Embora seja possível fazer Beans não visuais, a
composição é feita visualmente
Conceitos Básicos
• Uma ferramenta visual descobre as propriedades
métodos e eventos de um Bean usando
introspecção (olhar dentro do Bean)
• Dizemos que as propriedades, métodos e eventos
são expostos pelo Bean.
• Propriedades correspondem a atributos de
aparência e de comportamento que podem ser
mudados em tempo de Design
• Beans usam eventos para se comunicarem com
outros Beans
Conceitos Básicos
• A persistência permite que um Bean salve seu
estado e o restaure adiante
• JavaBeans usa Object Serialization para
implementar a persistência
• Os métodos de um Bean não são diferentes de
outros métodos em Java
• Criar e manipular Beans é muito simples e pode
ser feito por programadores humanos ou por
ferramentas de design
Propriedades
• Simple Properties:
– Representam tipos de dados simples
– Podem ser nativos (int, String, ...) ou não.
– Métodos devem se chamar
get<NomePropriedade> e set<NomePropriedade>
• Desta forma, a ferramenta visual infere que existe uma
propriedade chamada "cor" que pode ser lida (get) e
alterada (set):
public Color getCor();
public void setCor(Color cor);
Propriedades
• Indexed Properties:
– Contêm um array de valores possíveis
– Métodos getter e setter são como para
Simple Properties mas manipulam um array,
ou recebem um índice a mais
public
public
public
public
String[] getEstados()
String getEstados(int índice)
void setEstados(String[] estados)
void setEstados(int índice, String estado)
Propriedades
• Bound Properties
– Avisam outros objetos de mudanças nos seus
valores
– Uma mudança de valor gera um evento
PropertyChangeEvent
– Listeners deste evento serão notificados da
mudança
Propriedades
• Constrained Properties
– São como Bound Properties mas os Listeners
podem vetar a mudança
– Uma mudança aqui gera um evento
VetoableChangeEvent
Eventos
• Os eventos possíveis são:
– PropertyChangeEvent (para mudanças em
Bound Properties)
– VetoableChangeEvent (para mudanças em
Constrained Properties)
Eventos
• Quem gera eventos PropertyChangeEvent deve
implementar os seguintes métodos:
public void addPropertyChangeListener(PropertyChangeListener pcl);
public void removePropertyChangeListener(PropertyChangeListener pcl);
• Quem gera eventos VetoableChangeEvent deve
implementar os seguintes métodos:
public void addVetoableChangeListener(VetoableChangeListener vcl);
public void removeVetoableChangeListener(VetoableChangeListener vcl);
Eventos
• Quem gera eventos customizados deve implementar os
seguintes métodos:
public void add<Custom>Listener(<Custom>Listener cl);
public void remove<Custom>Listener(<Custom>Listener cl);
public void addMeuListener(MeuListener cl);
public void removeMeuListener(MeuListener cl);
Eventos
• Os Beans Listener devem implementar a
interface apropriada:
– PropertyChangeListener:
• Deve implementar o método PropertyChange
(PropertyChangeEvent e)
– VetoableChangeListener:
• Deve implementar o método VetoableChange
(VetoableChangeEvent e)
– <Custom>Listener:
• Deve implementar o método public Xpto
(<CustomEvent> e);
Criando um evento
1. Crie uma classe de evento:
•
Crie ou estenda uma classe e a nomeie (ex:
ActionEvent)
2. Crie um event listener:
•
Crie ou use uma interface e uma classe que a
implemente (ex: ActionListener)
3. Escreva a classe que origina o evento:
•
Adicione os método addXXListener e
removeXXListener
4. Escreva uma classe conectora:
•
Crie uma classe que registre os eventos
Exemplo: Button Handler
1. Crie uma classe de evento:
• Vamos utilizar a classe ActionEvent já existente
na JDK do Java
2. Crie um event listener:
• Vamos usar a interface ActionListener já
existente na JDK do Java
• Vamos criar uma classe chamada
ButtonHandler a qual irá implementar a interface
ActionListener
2. Crie um event listener:
public class ButtonHandler implements ActionListener {
// componente que irá conter mensagens sobre os eventos gerados
private JTextArea output;
// colocará as mensagens no textarea a cada evento recebido
public ButtonHandler( JTextArea output ) {
this.output = output;
}
// Ao receber uma notificação de evento, a mensagem será appended
public void actionPerformed( ActionEvent event ) {
this.output.append( "Action occurred in the Button Handler: "
+ event + '\n' );
}
}
3. Escreva a classe que origina o evento
• Vamos usar a classe Button já existente na JDK
do Java
• A classe Button já possui os métodos:
– addActionListener
– removeActionListener
4. Escreva uma classe conectora
• Crie as instancias das classes a serem usadas
• Registre o tratador do evento ao causador do
evento
4. Escreva uma classe conectora
public class ActionEventExample {
public static void main(String[] args) {
JFrame frame = new JFrame( "Button Handler" );
JTextArea area = new JTextArea( 6, 80 );
// criar o objeto gerador do evento
JButton button = new JButton( "Fire Event" );
// registrar um objeto ActionListener à origem do evento
button.addActionListener( new ButtonHandler( area ) );
frame.add( button, BorderLayout.NORTH );
frame.add( area, BorderLayout.CENTER );
frame.pack();
frame.setDefaultCloseOperation( WindowConstants.DISPOSE_ON_CLOSE);
frame.setLocationRelativeTo( null );
frame.setVisible( true );
}
}
O que ocorre quando um evento
acontece?
• A origem do evento invoca o método handler do
evento, o qual é esperado pelo eventListener
registrado à origem
– No nosso exemplo o acionamento do botão
reage ao método actionPerformed do
ButtonHandler
Um Bean mínimo
• Tem um construtor default
• Implementa a interface Serializable
• Para poder salvar o Bean customizado em tempo
de design
• Para entender melhor isso, veja a diferença entre
a forma de instanciar objetos sem e com
componentização
– Quando usamos componentização, objetos podem ser
instanciados em tempo de design e seus atributos
podem ser alterados em tempo de design
– É necessário ter serialização para poder obter os objetos
novamente em tempo de execução
Exemplo: SimpleBean
import java.awt.Color;
import java.beans.XMLDecoder;
import javax.swing.JLabel;
import java.io.Serializable;
public class SimpleBean extends JLabel implements Serializable {
public SimpleBean() {
setText( "Hello world!" );
setOpaque( true );
setBackground( Color.RED );
setForeground( Color.YELLOW );
setVerticalAlignment( CENTER );
setHorizontalAlignment( CENTER );
}
}
Classes especiais para ajudar
• PropertyChangeSupport e
VetoableChangeSupport
– Ajudam a mandar os eventos para todos os
listeners e outras tarefas semelhantes
Introspecção
• É o processo automático de análise do bean, o
qual, revela as suas propriedades, eventos e
métodos
• É suportado normalmente pela reflexão (comum
ao Java), através de nomeação de métodos
seguindo uma padronização:
– set/getPropriedade();
– add/removeListener();
Coisas que podem ser encontradas
através de introspecção
• Simple property
– public void setPropertyName(PropertyType value);
– public PropertyType getPropertyName();
• Boolean property
– public void setPropertyName(boolean value);
– public boolean isPropertyName();
• Indexed property
–
–
–
–
public void setPropertyName(int index, PropertyType value);
public PropertyType getPropertyName(int index);
public void setPropertyName(PropertyType[] value);
public PropertyType[] getPropertyName();
Coisas que podem ser encontradas
através de introspecção
• Events
– public void addEventListenerType(EventListenerType l);
– public void removeEventListenerType(EventListenerType l);
– public void addEventListenerType(EventListenerType l)
throws TooManyListenersException;
– public void removeEventListenerType(EventListenerType l)
throws TooManyListenersException;
• Methods
– public methods
Persistência
• Através de serialização
• Serialização de um objeto implica em converter o
objeto em uma stream de dados e armazená-lo
• Qualquer aplicação que use o bean pode assim
“reconstituí-lo” através de uma deserialização
Persistência com XML
• A persistência, através de serialização de um
componente JavaBeans pode ser implementado
através de XML
– Padrão para desenvolvimento
– Padrão para criar padrões
– Interpretação padronizada
XMLEncoder
• Permite que classes que geram objetos serializáveis
sejam escritas como arquivos XML:
XMLEncoder encoder = new XMLEncoder(
new BufferedOutputStream(
new FileOutputStream( "Beanarchive.xml") ) );
encoder.writeObject( object );
encoder.close();
XMLDecoder
• Permite ler um arquivo XML que foi decodificado pela
classe XMLEncoder:
XMLDecoder decoder = new XMLDecoder(
new BufferedInputStream(
new FileInputStream( "Beanarchive.xml" ) ) );
Object object = decoder.readObject();
decoder.close();
Criação de componentes com
JavaBeans:
Exemplo:
Controle Remoto de uma TV
Exemplo: Elementos Básicos
• Para a TV
• Um botão liga/desliga com luzinha mostrando o
estado on/off
• Botões Up/Down de mudança de canal
– Não modelados aqui para simplificar
• Uma tela
• Um display do número de canal
• Para o controle remoto
• Um botão liga/desliga com luzinha mostrando o
estado on/off
• Botões Up/Down de mudança de canal
• Uma ilha numérica (keypad)
O Bean BotaoTVOnOff
• Tem uma propriedade "on" (boolean)
• Ao ser alterada com setOn(boolean estado), a
luzinha muda de cor
• É uma propriedade simples (pode ser bound)
• Não avisa ninguém quando muda de valor
• É listener de outras propriedades "on“
• Observar que clicar no botão não chama
setOn()
O Bean BotaoComum
• Gera o evento ActionPerfomedEvent quando
clicado
• Não tem propriedade
O Bean KeyPadTv
• Tem 10 botões numéricos
• O próprio Bean atende aos cliques desses botões
e não os repassa para fora
• Tem uma área para mostrar o valor temporário
sendo digitado
• Tem um botão ENTER
• Para mudar a propriedade "valor" que recebe o
valor temporário
• A propriedade "valor" é bound para avisar outros
Beans da mudança
• Tem uma propriedade simples inteira informando o
número máximo de dígitos que o keypad manipula
O Bean Limites
• Recebe um VetoableChangeEvent e veta se o
valor não estiver nos limites apropriados
• Os limites inferior e superior são propriedades
simples
• Ao aceitar um valor, a propriedade bound "valor"
é alterada e os listeners são avisados
O Bean TVDisplay
• Trata da parte gráfica da TV
– Liga e desliga a tela
– Mostra o canal selecionado
• Propriedades simples "on" e "canal"
– Podem ser bound para ter generalidade (uso
futuro)
O Bean TVEstado
• Propriedade bound "on" com métodos
– getOn()
– setOn()
– onOff() // toggle da propriedade
• Propriedade Constrained "canal" com métodos
– getCanal()
– setCanal()
– incrementaCanal()
– decrementaCanal()
A Aplicação
Construção da aplicação: Criação
1. Arraste um BotaoOnOff para o controle remoto e
altere o label se desejar
2. Arraste dois OurButton para o controle remoto e
altere os labels para Up e Down
3. Arraste um KeyPad para o controle remoto e altere
o número de dígitos se desejar
4. Arraste um BotaoOnOff para a TV e altere o label
se desejar
5. Arraste um TVEstado, TVDisplay e Limites para a
TV
6. Altere os limites inferior e superior de Limites se
desejar
Construção da aplicação: Conexões
1. Liga/Desliga Remoto/ActionPerformed para
TVEstado/OnOff (gera hookup)
2. Liga/Desliga TV/ActionPerformed para
TVEstado/OnOff (gera hookup)
3. TVEstado/BindProperty/on para Liga/desliga
Remoto/on
4. TVEstado/BindProperty/on para Liga/desliga TV/on
5. TVEstado/BindProperty/on para TVDisplay/on
6. Up/ActionPerformed para
TVEstado/incrementaCanal (gera hookup)
Construção da aplicação: Conexões
7. Down/ActionPerformed para
TVEstado/decrementaCanal (gera hookup)
8. Keypad/BindProperty/valor para TVEstado/canal
9. TVEstado/Event/VetoableChange para
Limites/vetoableChange (gera hookup)
10. Limites/BindProperty/valor para TVDisplay/canal
Exercícios
• Adicionar as funcionalidades aos botões de
canais
• Adicionar uma propriedade volume e botões
para lidar com os volumes
• Adicionar uma imagem a ser exibida para cada
canal