Utilizando o padrão Fábrica para criar frames de testes
O objetivo deste artigo é criar uma fábrica de frames para diversos tipos de testes envolvendo interfaces gráficas. Os frames de testes nesse caso são utilizados apenas para exibir um determinado componente gráfico antes do mesmo ser adicionado à interface gráfica da aplicação.
Para melhor entendimento do artigo, é necessário ter uma noção básica do padrão Fábrica.
Introdução
Muitas vezes, durante o desenvolvimento de aplicações, o programador depara-se com certos componentes gráficos, formulários, entre outros, que não são abrangidos por frameworks. Em alguns casos como esses a interface gráfica deve ser feita manualmente.
Desenvolver interfaces gráficas manualmente custa tempo e paciência. Muitas vezes é necessário redimensionar a janela, alterar a posição dos componentes, mais de uma vez até chegar-se a um resultado satisfatório. Os frames de testes entram aí pra facilitar um pouco esse trabalho.
Imagine um JFrame que exiba em seu title, as dimensões da janela e, a medida que seja redimensionada, esse title exibe as novas dimensões. Com isso, o programador poderia lançar componentes neste JFrame e calcular, em tempo de execução, seu tamanho preferencial. É claro, quando o método pack() (que redimensiona a janela de acordo com os componentes adicionados) não for satisfatório.
Imagine um outro JFrame que receba um JPanel e permita que o programador o posicione livremente em tempo de execução, visualizando suas coordenadas.
Utilizando o padrão Fábrica, é possível criar uma biblioteca para criação de frames de testes que evite que o programador precise criar um JFrame toda vez que for preciso testar uma interface gráfica.
Neste artigo serão visualizados apenas dois frames de testes: frame com JPanel e recursos de redimensionamento e um mais simples, que apenas exibe um JDesktopPane. Mas como o padrão utilizado permite a criação de inúmeros frames, fica a parte de cada programador criar funcionalidades necessárias às suas aplicações.
A Fábrica
A classe responsável por criar as instâncias de JFrame, será a JFrameFactory, como mostra a Listagem 01.
package br.com.jm.factory;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JPanel;
import br.com.jm.jframes.*;
public abstract class JFrameFactory extends JFrame {
public abstract void start();
public static JFrameFactory createJFrame(Object object){
if(object instanceof JDesktopPane){
JDesktopPane jDesktopPane = (JDesktopPane)object;
return new JFrameWithJDesktopFrame(jDesktopPane);
}
if(object instanceof JPanel){
JPanel jPanel =(JPanel)object;
return new JFrameWithPanel(jPanel);
}
return null;
}
}
Listagem 01 – A classe JFrameFactory
A própria fábrica herda JFrame pois seu principal método de retorno createJFrame irá retornar suas subclasses de acordo com o argumento passado.
O método abstrato start() deverá ser implementado pelas subclasses de JFrameFactory e representa as ações que o frame irá tomar antes de ser exibido. Em geral, para aplicações mais simples, apenas uma chamada ao método setVisible(true) já é suficiente.
O método static createJFrame(Object o) recebe um Objeto e analisa de que instância o mesmo é. É aqui que o padrão Fábrica realiza sua principal funcionalidade, que é cuidar da instanciação de classes. Ou seja, o cliente apenas solicita à fábrica que retorne o objeto responsável por atuar no argumento passado.
Caso o Objeto não seja abrangido pela Fábrica, este método retorna null.
Com isso tem-se a fábrica de frames de testes bem simples, apenas com 2 possibilidades de retorno. Mas como dito no início do artigo, isto é apenas um pontapé para uma verdadeira fábrica com inúmeras possibilidades de criação de frames.
Criando os frames
Agora é a hora de criar os frames de testes retornados pela Fábrica. As listagens 02 e 03 exibem o JFrameWithJDesktopFrame e JFrameWithPanel, respectivamente.
package br.com.jm.jframes;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import br.com.jm.factory.JFrameFactory;
public class JFrameWithJDesktopFrame extends JFrameFactory {
public JFrameWithJDesktopFrame(JDesktopPane jDesktopPane){
super.getContentPane().add(jDesktopPane);
super.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
super.setExtendedState(JFrame.MAXIMIZED_BOTH);
}
public void start() {
super.setVisible(true);
}
}
Listagem 02 – JFrameWithJDesktopFrame
package br.com.jm.jframes;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import br.com.jm.factory.JFrameFactory;
public class JFrameWithPanel extends JFrameFactory implements ComponentListener{
public JFrameWithPanel(JPanel jPanel){
super.addComponentListener(this);
super.getContentPane().add(jPanel);
super.pack();
super.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
private void refreshTitle(){
Dimension size = super.getSize();
Point location = super.getLocation();
String tString = String.format("dimensoes: %.0f x %.0f, localizacao: x = %.0f, y = %.0f",
size.getWidth(), size.getHeight(), location.getX(), location.getY());
super.setTitle(tString);
}
public void start(){
super.setVisible(true);
}
public void componentMoved(ComponentEvent arg0){
//atualiza o título
refreshTitle();
}
public void componentResized(ComponentEvent arg0){
//atualiza o título
refreshTitle();
}
public void componentHidden(ComponentEvent arg0){}
public void componentShown (ComponentEvent arg0){}
}
Listagem 03 – JFrameWithPanel
A Listagem 02 é extremamente simples de ser entendida, visto que a classe apenas recebe um JDesktopPanel, e por se tratar de uma subclasse de JFrameFactory que é uma subclasse de JFrame, chama o método getContentPane().add(jDesktopPane) para adicionar o desktoppane em sua janela. E em seu método start() apenas uma chamada a setVisible(true) é suficiente.
Já na Listagem 03 foi acrescentada uma das funcionalidades citadas no início do artigo, que é a de exibir, em tempo de execução, as dimensões da janela à medida que a mesma é redimensionada (além de sua posição x e y). Para isso utilizou-se a interface ComponentListener, implementando componentMoved e componentResized, ambos chamando o método refreshTitle para atualizar o título do frame ao ser redimensionado ou movido.
Com isso tem-se dois diferentes frames de testes; os mesmos retornados pela fábrica. O que possibilita ao programador rapidamente testar um panel com poucas linhas de código como indicado no próximo tópico, poupando tempo no desenvolvimento da aplicação.
Testando a fábrica
Vale lembrar que o ideal para esta fábrica é ser compilada em um jar e importada pelo projeto enquanto o mesmo é desenvolvido a fim de ser utilizada para testes e cálculos de variáveis responsáveis pelo layout quando é necessário criar uma interface gráfica manualmente. Porém, apenas para concluir a idéia do artigo ela será testada sem haver compilação em .jar.
A Listagem 04 mostra apenas um teste envolvendo um JPanel e um JDesktopPane. É possível perceber que o método createJFrame retorna diferentes frames, visto que foi a fábrica quem decidiu a respeito da instância que irá tratar o objeto.
import java.awt.BorderLayout;
import java.awt.Dimension;
import javax.swing.JDesktopPane;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.UIManager;
import br.com.jm.factory.JFrameFactory;
public class Teste_Factory {
public static void main(String[] args){
try{
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch(Exception exception){
}
JPanel jPanel = new JPanel(new BorderLayout());
jPanel.add(new JLabel("teste-factory"), BorderLayout.NORTH);
jPanel.add(new JLabel("teste-1:"), BorderLayout.WEST);
jPanel.add(new JTextField(), BorderLayout.CENTER);
jPanel.add(new JLabel("teste-fim"), BorderLayout.SOUTH);
jPanel.setPreferredSize(new Dimension(400,500));
JDesktopPane jDesktopPane = new JDesktopPane();
JFrameFactory jFrame = JFrameFactory.createJFrame(jPanel);
jFrame.start();
JFrameFactory jFrame2 = JFrameFactory.createJFrame(jDesktopPane);
jFrame2.start();
}
}
Listagem 03 – Teste envolvendo um JPanel e um JDesktopPane.
Conclusão
Utilizando o padrão fábrica foi possível criar diferentes instâncias de JFrame onde cada uma possuía uma funcionalidade diferente. Os frames nesse caso foram usados apenas para se testar e calcular coordenadas referentes ao layout dos componentes gráficos. Sendo assim, por se tratar de um padrão de projeto bem definido e estruturado, permite ao desenvolvedor criar inúmeros frames de testes de acordo com as necessidades da aplicação e adicioná-los na fábrica, sem necessidade de alteração no restante do código.
Um grande abraço,
Adriano S. Castro