Aguarde...

MVC com Java e desktop (parte 3)

Publicado em 09/07/2012

Interface gráfica

Nessa parte do projeto, iremos desenvolver a interface gráfica, mas apenas no que se refere aos componentes, nenhum tipo de comportamento ou interação com o usuário será implementado.

O sistema terá duas telas, uma principal onde será exibido o resumo do processo de ordenação e outra onde será configurado o número a ser ordenado. As duas telas irão se interagir em alguns pontos (vocês verão a importância da controller quando isso ocorrer). Segue abaixo o código da tela principal. Observando que, como o foco do artigo não é interface gráfica, as telas foram projetadas de forma simplificada para facilitar o entendimento.

                        package br.com.gqferreira.view;

                        import java.awt.Color;
                        import javax.swing.JButton;
                        import javax.swing.JFrame;
                        import javax.swing.JLabel;
                        import javax.swing.JScrollPane;
                        import javax.swing.JTextField;
                        import javax.swing.JTextPane;

                        /**
                         *
                         * @author Gustavo Ferreira
                         */
                        public class FramePrincipal extends JFrame {

                            private JLabel lbNumeroMaximo;
                            private JLabel lbNumeroGerado;
                            private JLabel lbNumeroOrdenado;
                            private JLabel lbQtdeTrocas;
                            private JTextField tfNumeroMaximo;
                            private JTextField tfNumeroGerado;
                            private JTextField tfNumeroOrdenado;
                            private JTextField tfQtdeTrocas;
                            private JTextPane tpInformacao;
                            private JScrollPane spRolagemPassos;
                            private JButton btGerarNumero;
                            private JButton btOrdernarNumero;

                            //Get and Set
                            public JButton getBtGerarNumero() {
                                return btGerarNumero;
                            }
                            public JButton getBtOrdernarNumero() {
                                return btOrdernarNumero;
                            }
                            public JTextField getTfNumeroGerado() {
                                return tfNumeroGerado;
                            }
                            public JTextField getTfNumeroMaximo() {
                                return tfNumeroMaximo;
                            }
                            public JTextField getTfNumeroOrdenado() {
                                return tfNumeroOrdenado;
                            }
                            public JTextField getTfQtdeTrocas() {
                                return tfQtdeTrocas;
                            }
                            public JTextPane getTpInformacao() {
                                return tpInformacao;
                            }
                            public JScrollPane getSpRolagemPassos() {
                                return spRolagemPassos;
                            }  

                            public FramePrincipal() {

                                this.setTitle("MVC");
                                this.setDefaultCloseOperation(EXIT_ON_CLOSE);
                                this.setResizable(false);
                                this.setSize(380, 320);
                                this.setLocationRelativeTo(null);
                                this.setLayout(null);

                                this.lbNumeroMaximo = new JLabel("Numero maximo");
                                this.lbNumeroGerado = new JLabel("Numero gerado");
                                this.lbNumeroOrdenado = new JLabel("Numero ordenado");
                                this.lbQtdeTrocas = new JLabel("Qtde de trocas");

                                this.tfNumeroMaximo = new JTextField("1");
                                this.tfNumeroMaximo.setEnabled(false);
                                this.tfNumeroGerado = new JTextField();
                                this.tfNumeroGerado.setEnabled(false);
                                this.tfNumeroOrdenado = new JTextField();
                                this.tfNumeroOrdenado.setEnabled(false);
                                this.tfQtdeTrocas = new JTextField();
                                this.tfQtdeTrocas.setEnabled(false);

                                this.btGerarNumero = new JButton("Gerar numero randomico");
                                this.btOrdernarNumero = new JButton("Ordenar numero gerado");
                                this.btOrdernarNumero.setEnabled(false);

                                this.tpInformacao = new JTextPane();
                                this.tpInformacao.setText("Esse sistema ordena uma sequencia numerica atravez do algoritmo bolha (buble sort).\n\nEsse algoritmo realiza trocas entre um numero e o seu imediato caso este seja menor que o proximo.");
                                //Evita que o texto do TextPane seja selecionavel
                                this.tpInformacao.setEnabled(false);
                                //Define a cor do texto
                                this.tpInformacao.setDisabledTextColor(Color.BLACK);
                                //Tira o fundo branco padrao do TextPane
                                this.tpInformacao.setOpaque(false);

                                this.spRolagemPassos = new JScrollPane();
                                this.spRolagemPassos.setBorder(null);
                                this.spRolagemPassos.setViewportView(this.tpInformacao);

                                this.lbNumeroMaximo.setBounds(20, 20, 150, 20);
                                this.lbNumeroGerado.setBounds(20, 85, 150, 20);
                                this.lbNumeroOrdenado.setBounds(20, 150, 150, 20);
                                this.lbQtdeTrocas.setBounds(20, 215, 150, 20);

                                this.tfNumeroMaximo.setBounds(20, 50, 100, 25);
                                this.tfNumeroGerado.setBounds(20, 115, 100, 25);
                                this.tfNumeroOrdenado.setBounds(20, 180, 100, 25);
                                this.tfQtdeTrocas.setBounds(20, 245, 100, 25);

                                this.btGerarNumero.setBounds(150, 20, 200, 25);
                                this.btOrdernarNumero.setBounds(150,55, 200, 25);

                                this.spRolagemPassos.setBounds(150, 90, 200, 180);

                                this.add(lbNumeroMaximo);
                                this.add(lbNumeroGerado);
                                this.add(lbNumeroOrdenado);
                                this.add(lbQtdeTrocas);

                                this.add(tfNumeroMaximo);
                                this.add(tfNumeroGerado);
                                this.add(tfNumeroOrdenado);
                                this.add(tfQtdeTrocas);

                                this.add(btGerarNumero);
                                this.add(btOrdernarNumero);

                                this.add(spRolagemPassos);
                            }
                        }

A outra tela, como dito anteriormente, será onde se configura o número a ser ordenado, segue abaixo.

                        package br.com.gqferreira.view;

                        import javax.swing.JButton;
                        import javax.swing.JDialog;
                        import javax.swing.JLabel;
                        import javax.swing.JSlider;

                        /**
                         *
                         * @author Gustavo Ferreira
                         */
                        public class DialogGerarNumero extends JDialog{

                            private JLabel lbNumeroMaximo;
                            private JButton btGerar;
                            private JButton btGerarOrdenar;
                            private JSlider slNumeroMaximo;

                            //Get and Set
                            public JButton getBtGerar() {
                                return btGerar;
                            }
                            public JButton getBtGerarOrdenar() {
                                return btGerarOrdenar;
                            }    
                            public JSlider getSlNumeroMaximo() {
                                return slNumeroMaximo;
                            }

                            public DialogGerarNumero() {
                                this.setSize(300, 205);
                                this.setLocationRelativeTo(null);
                                this.setTitle("Gerar numero randomico");
                                this.setModal(true);
                                this.setLayout(null);
                                this.setResizable(false);
                                this.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);

                                this.lbNumeroMaximo = new JLabel("Numero maximo da sequencia");

                                this.slNumeroMaximo = new JSlider(1, 99999);
                                this.slNumeroMaximo.setMajorTickSpacing(9999);
                                this.slNumeroMaximo.setPaintTicks(true);

                                this.btGerar = new JButton("Gerar numero");
                                this.btGerarOrdenar = new JButton("Gerar e ordenar");

                                this.lbNumeroMaximo.setBounds(20, 20, 200, 20);
                                this.slNumeroMaximo.setBounds(20, 50, 245, 30);
                                this.btGerar.setBounds(70, 90, 150, 25);
                                this.btGerarOrdenar.setBounds(70, 125, 150, 25);

                                this.add(lbNumeroMaximo);
                                this.add(slNumeroMaximo);
                                this.add(btGerar);
                                this.add(btGerarOrdenar);
                            }
                        }

Controller

Dependência da controller

Dependência da controller

Agora é hora de implmentar a controller. Se você copiou as classes da interface gráfica e colou ao seu projeto e configurou o método main, deve ter notado que só a tela principal abriu e ainda sim não foi possível interarir por falta de uma classe controladora de eventos, a camada controller.

Como as duas telas irão se interagir e ambas copartilharão de alguns métodos, o correto é fazer com que elas usem a mesma controller, pois assim a interação de uma interferirá na outra.

                        package br.com.gqferreira.controller;

                        import br.com.gqferreira.model.bo.OrdenacaoBO;
                        import br.com.gqferreira.model.vo.Ordenacao;
                        import br.com.gqferreira.view.DialogGerarNumero;
                        import br.com.gqferreira.view.FramePrincipal;
                        import java.awt.event.ActionEvent;
                        import java.awt.event.ActionListener;
                        import javax.swing.event.ChangeEvent;
                        import javax.swing.event.ChangeListener;

                        /**
                         * @author Gustavo Ferreira
                         * @see Classe que cria objeto de controle entre a camada Model e View
                         */
                        public class ControllerPrincipal implements ActionListener, ChangeListener {

                            private FramePrincipal framePrincipal;
                            private DialogGerarNumero dialogGerarNumero;

                            /**
                             * Construtor
Recebe o objeto da FramePrincipal para 'observer' seu comportamento, * tratar os eventos e redirecionar para a model. * @param framePrincipal */ public ControllerPrincipal(FramePrincipal framePrincipal) { this.framePrincipal = framePrincipal; //Definindo os listeners para os botoes dessa view. this.framePrincipal.getBtGerarNumero().addActionListener(this); this.framePrincipal.getBtOrdernarNumero().addActionListener(this); } //Evento de acao, pressionar um botao ou um [Enter] em inputs @Override public void actionPerformed(ActionEvent e) { /** * Se for o pressionar do botao 'Gerar Numero' da FrameMain, instancia uma * DialogGerarNumero */ if (e.getSource() == this.framePrincipal.getBtGerarNumero()) { //Instanciando a DialogGerarNumero this.dialogGerarNumero = new DialogGerarNumero(); this.dialogGerarNumero.getSlNumeroMaximo().setValue( Integer.parseInt(this.framePrincipal.getTfNumeroMaximo().getText())); //Registrando os listeners do Dialog this.dialogGerarNumero.getBtGerar().addActionListener(this); this.dialogGerarNumero.getBtGerarOrdenar().addActionListener(this); this.dialogGerarNumero.getSlNumeroMaximo().addChangeListener(this); this.dialogGerarNumero.setVisible(true); //Destruo o Dialog this.dialogGerarNumero = null; } else if (this.dialogGerarNumero != null) { //Eventos do DialogGerarNumero if (e.getSource() == this.dialogGerarNumero.getBtGerar()) { gerarNumero(); } else if (e.getSource() == this.dialogGerarNumero.getBtGerarOrdenar()){ gerarNumero(); ordenarNumero(); } } else if (e.getSource() == this.framePrincipal.getBtOrdernarNumero()){ ordenarNumero(); } } @Override public void stateChanged(ChangeEvent e) { if (this.dialogGerarNumero != null) { /** * A medida que o Slider eh arrastado, o campo equivalente na FramePrincipal * tem seu valor alterado */ if (e.getSource() == this.dialogGerarNumero.getSlNumeroMaximo()) { this.framePrincipal.getTfNumeroMaximo().setText( String.valueOf(this.dialogGerarNumero.getSlNumeroMaximo().getValue())); } } } /** * Metodo responsavel em controlar a acao de ordenacao. Redireciona para a Model */ private void ordenarNumero(){ //Manda ordenar e recebe uma Ordenacao como resultado do processo. Ordenacao ordenacao = new OrdenacaoBO().bubbleSort( Integer.parseInt(this.framePrincipal.getTfNumeroGerado().getText())); //Atualiza a view com o resultado this.framePrincipal.getTfNumeroOrdenado().setText( String.valueOf(ordenacao.getNumeroOrdenado())); this.framePrincipal.getTfQtdeTrocas().setText( String.valueOf(ordenacao.getQtdeTrocas())); } /** * Metodo que limpa os campos que contem valores resultados de uma ordenacao */ private void limparDadosOrdenacaoAnterior() { this.framePrincipal.getTfNumeroOrdenado().setText(null); this.framePrincipal.getTfQtdeTrocas().setText(null); } /** * Metodo que gera um numero randomico e atualiza a view.
* Executa logo apos o pressionar do botao 'Gerar' do DialogGerarNumero */ private void gerarNumero() { limparDadosOrdenacaoAnterior(); //Fecha o DialogGerarNumero this.dialogGerarNumero.setVisible(false); //Atualiza o numero na FramePrincipal this.framePrincipal.getTfNumeroGerado().setText(String.valueOf((int) (Math.random() * this.dialogGerarNumero.getSlNumeroMaximo().getValue()))); //Destroi a DialogGerarNumero this.dialogGerarNumero = null; //Habilita o botao 'Ordenar' da FramePrincipal this.framePrincipal.getBtOrdernarNumero().setEnabled(true); } }

Você deve ter percebido que quando implementamos somente a camada model não havia erros de compilação e o sistema até funcionava, mostrando que a model não dependia essencialmente de mais nada para funcionar. Também deve ter percebido que quando implementou a view, não havia erros de compilação mostrando que a camada view desconhecia todo o resto e poderia ser encaixado em qualquer sistema que precisasse de uma view como aquela. Já no caso da controller, esta sim está amarrado com tudo. Sozinha a controller não é nada e dá erros de compilação na falta do resto pois, ela tem o papel de "juntar as partes". Outra coisa interessante da controller é que é ela a responsável em tratar os erros de execução, nenhuma classe da model trata os tais erros.

Segue o link para baixar o código fonte e o sistema já compilado, fiquem a vontade para comentar abaixo.

Licença Creative Commons
MVC com Java e desktop (parte 3) de Gustavo Ferreira é licenciado sob uma Licença Creative Commons Atribuição-CompartilhaIgual 3.0 Não Adaptada.

Código fonte

Executável

Comentarios (26) para MVC com Java e desktop (parte 3)

Augusto Nunes,   09/05/2013

Prezado Gustavo,

Parabéns pelo artigo! Você conseguiu explicar de forma simples, clara e objetiva o que eu vinha procurando entender a muito tempo e com a exemplificação ficou 100% !!!

Abraços,

Augusto

Gustavo Ferreira,   09/05/2013

www.gqferreira.com.br

Obrigado, fico feliz que tenha gostado. Sinta-se a vontade para continuar navegando pelos artigos.

Agraços,
Gustavo Ferreira.

Augusto Nunes,   28/05/2013

Bom Dia, Gustavo. Tenho a seguinte dúvida, e gostaria da sua opinião/visão sobre a mesma:

- Estou desenvolvendo um sistema onde as classes envolvidas não são "Pessoas", "Pedidos", "Itens_Pedido", etc., mas sim "Editor de Textos", "Player de Vídeo", "Player de Áudio" e "Área de Exibição de Imagens". Este sistema deve permitir aos atores gerar seus próprios textos, simultaneamente gravar / reproduzir áudio / vídeo, etc...

Como seria a organização dessas classes em na arquitetura MVC? Quem seriam as classes BO, VO e Controller?

Desculpe-me pela falta de conhecimento, mas deveria eu pensar que este é o desenvolvimento de um "Framework" ???

Agradecendo qualquer esclarecimento,

atenciosamente

Augusto Cesar Nunes

Gustavo Ferreira,   30/05/2013

www.gqferreira.com.br

Nesse caso eu imagino que "Player de Vídeo", "Editor de Texto" entre outros possam ser módulos independente, cada um com sua estrutura MVC. Se os módulos precisarem trabalhar em conjunto uns com os outros, você precisará que todos tenham uma ligação com a mesma controller para que as ações de um módulo interfira no outro.

Ou (melhor)...

Não precisam ser módulos independentes. Você pode ter uma DAO para audio (grava e lê), outra para video (grava e lê), uma para texto (grava e lê) e outra para imagens (grava e lê). Uma BO que centraliza os acessos à DAO e que resolva algum processo (conversao de formatos de audio e/ou video, redimensionamento de imagens, efeitos, restrição de acessos, login, logs, etc). Você poderá ter uma view para cada parte do sistema e uma que centralize. Olhe nessa imagem o que imaginei.









As VOs podem ser classes separadas para cada tipo de arquivo a ser salvo, como por exemplo: Audio.java contendo nome, duração, titulo, bytes[], formato, etc...

Carlos Soares,   20/08/2013

Muito bom mesmo, agora ficou muito mais claro.
Valew Gustavo.

Misael C. Homem,   10/10/2013

Boa tarde Gustavo,

Parabéns pelo artigo. Só estou com uma dúvida do projeto todo, e se puder sanar agradeço.
Sabe-se que na camada model se insere as classes do modelo, e que no caso as classes tipo VO apenas tratam de valores. Ate aqui blz, a questão é se na nas classes de BO contém as regras de negócio então o trecho:

public String getNome() {
return "Sr. "+nome;
}

A regra de prefixar o valor "Sr. " antes do nome não deveria ser posta dentro de uma BO ?
Eu entendi o por que tu colocou este exemplo, obviamente para explicar o conceito get e set, mas num cenário real o mais correto seria ter essa regra em uma classe do tipo BO, correto ?

Fico no aguardo.

Misael

Misael C. Homem,   10/10/2013

Gustavo,

Tenho mais 3 questão relevantes:

1) Em um sistema em que se aplique o MVC, onde (qual o nome da pasta) ficariam arquivos de recurso da aplicação tais como ícones, figuras, imagens, sons, etc ?
OBS: em uma app que tenho aqui de modelo eu criei uma pasta chamada resource para comportar tais arquivos!

2) Na web e até em alguns livros, os arquivos do tipo .jar oriundos de classes de conexão por exemplo MySql em geral são postos em uma pasta chamada lib para serem referenciados pela aplicção no caso inclusos como jar, utilizando o MVC isto estaria correto ou se deveria por em uma pasta com nomeação diferente?

3) Poderia alterar o teu modelo incluindo estas questões ?

Gustavo,   16/10/2013

www.gqferreira.com.br

Respondendo a primeira dúvida...

A VO é uma classe que também está no pacote model, portanto também possui alguns comportamentos de negócio. E como é uma classe que visa encapsular os atributos, deixar essa regra do "Sr" fora pode por abaixo o encapsulamento. Imagine que a classe Pessoa possa ser instanciada em várias métodos de uma BO, você vai precisar fazer o tratamento do "Sr" idêntico em todos os pontos. Isso acabaria aniquilando também o objetivo dos métodos controladores de acesso (get e set).

As classes do tipo BO devem se preocupar mais com o "algoritmo" da coisa, a engine. Mas você pode criar dois get's, um com tratamento e outro sem, assim, quando precisar alterar o tratamento, todo mundo que utilize esse get será afetado, deixando de afetar aos métodos que optarem por utilizar o get 'puro'

Gustavo,   16/10/2013

www.gqferreira.com.br

1) Por entender que tais recursos fazem parte da view, eu crio uma pasta de resources dentro do pacote view:
br.com.gqferreira.projectname.view.resources.[images|sounds|videos|icons]

2) Não precisa se preocupar em colocar as bibliotecas na estrutura MVC do teu projeto, até porque internamente cada biblioteca deve possuir a sua própria estrutura, o que dificultaria na hora de julgar se é model, se é controller... se é a DAO da model ou se... enfim, a biblioteca provavelmente é mista. Coloque em uma pasta chamada lib mesmo.

3) O que você sugere que possa ser alterado no modelo?

Obrigado pela atenção,
Gustavo Ferreira.

Diego,   06/03/2015

Boa tarde Gustavo,

Gostei muito desse artigo ,
muito bem explicado e consegui entender de primeira rs ..

sou iniciante e ajudou muito para poder projetar meus projetos
vlw cara ..

Gustavo,   06/03/2015

www.gqferreira.com.br

Aqui ainda é bom dia Diego rsrs..

Que bom que você tenha achado útil o artigo, fico contente.

Abraços!

Cid Reis,   12/04/2015

Parabéns pelo o artigo tirou varias duvidas porem surgiu outras rsrsrs

no padrao de desenvolvimento web notei em varios tutorias e artigos que muda bastante esses padroes onde entrar como exemplo:

Util
Filter

vilson,   20/05/2015

E o controller precisa conhecer as duas interfaces!? e se uma terceira, quarta , quinta forem adicionadas?

Yuri Caldas,   28/07/2015

Olá Gustavo, gostei bastante do seu artigo.

Olhá só, estou com um problema, criei 2 classes controller, 1 para controlar um login e outra pra controlar a tela de configuração de conexão com o Banco de Dados.

Quando executo o sistema, a tela de login se abre, aí eu clico no botão para abrir a tela de configuração.

Ao abrir a tela de configuração, quando vou sair dela, dou um dispose() e instancio uma nova classe da controller do login.

O problema é, quando clico em fechar ou pra sair da nova tela de login, a tela somente some, mas o sistema continua em execução.

Como posso resolver isso? Tem alguma ideia?

Obrigado

CARLOS HENRIQUE COSTA CAPISTRANO,   06/10/2015

Parabéns muito bom.

Memoriacache,   07/10/2015

www.memoriacache.com.br

Para quem se interessar, aqui tem outra aplição desktop com netbeans: http://www.memoriacache.com.br/aplicacoes-desktop-com-netbeans-swing/

Sidnei Favoreto Vieira,   22/10/2015

Olá Gustavo.

Parabéns pelo artigo, esclareceu várias duvidas, mas ainda ficaram algumas.

Por um acaso não teria um exemplo de sistema que grave em banco e valide campos da tela para ter uma ideia não né?

Hoje eu tenho um sistema com uma tela padrão que valida de forma automática os campos, e as minhas telas de cadastro eu herdo ela.

Queria saber como fazer isso usando MVC.

Acredito que eu tendo um exemplo de um sistema que faça algumas validações e grave em banco eu consigo fazer o meu.

Obrigado.

Att,
Sidnei

comentario,   16/01/2016

Muito boa a didática, direta ao ponto e demonstra de forma muito clara. parabéns.

Sidney Francisco,   15/03/2016

Artigo excelente!! Já pesquisei bastante para obter um exemplo de MVC. De todos que venho pesquisando, considerei esse, o melhor!! Obrigado!! Um abraço!

Monique,   02/06/2016

Me ajudou muito. Ótimo artigo, parabéns!

Gabriel Paiva,   10/08/2016

Otima explicação. Clareou muito para mim.

Olá Gustavo.

Muito obrigado por este artigo, estou começando agora com o MVC, e com esta primeira parte do seu artigo estou apreciando a utilidade deste padrão de projeto.

Custódio,   28/11/2016

Muito bom e agradeço pela explicação, procurei bastante como fazer MVC e final achei e bem explícito

Junior,   19/12/2016

Parabéns. Me ajudou muito, o assunto abordado foi muito bem explicado e sanou muitas dúvidas que tinha. Muito obrigado, um grande abraço e sucesso.

Mayara Alves,   13/01/2017

Parabéns pelo artigo, depois de pesquisar muito finalmente encontrei um artigo que me fez entender claramente sobre o assunto, ótima didática. Muito obrigada!

Michel Souza,   10/02/2018

Boa tarde Gustavo Ferreira, gostaria de poder utilizar esse artigo em um documento em PDF totalmente didático, sem fins lucrativos que estou desenvolvendo, para ajudar iniciantes assim como eu na programação em Java, utilizando o modelo MVC. Não consegui encontrar em pesquisas explicações tão boas, assim como as suas, se poderei utilizar, deixarei respectivamente a fonte do seu site e uma parte dizendo que o Artigo é de sua autoria e dos demais autores com suas respectivas fontes. Obrigado pela atenção!

Gostou do artigo? Acha que poderia ser melhor em algum ponto? Dê o seu feedback!