Mãos à obra!
Vamos agora desenvolver um software que implemente o MVC usando apenas a camada model, mais a frente terminaremos o software adicionando as outras camadas. Esse software será capaz de ordenar uma sequência de dígitos numéricos e salvar dois arquivos de texto, um contendo todo os passos que foram nescessários para que os números fossem ordenados e um outro com o resultado.
Primeiro, vou considerar que você já tenha o Java instalado na sua máquina e que saiba se virar com alguma IDE ou utilize um editor de texto comum (se você for guerreiro!), eu vou utilizar o NetBeans.
Para começarmos, crie um projeto Java-SE (Standard Edition, aplicação desktop). No meu caso o projeto se chama 'MVC'. Em seguinda crie uma estrutura de pastas com os seguintes nomes:
- br.com.gqferreira
- br.com.gqferreira.controller
-
br.com.gqferreira.model
- br.com.gqferreira.model.bo
- br.com.gqferreira.model.dao
- br.com.gqferreira.model.vo
- br.com.gqferreira.controller
- br.com.gqferreira.view
Por padrão a estrutura de pastas são nomeadas com o domínio da empresa ou do desenvolvedor de forma invertida (br.com.gqferreira). Se você ainda não tem um domínio, invente! Coloque o seu nome ou o do projeto com diretório principal.
Se você criou os diretórios através de uma IDE, irá notar que na prática foi criado mais diretórios do que parecem. Se você navegar pelas pastas fora da IDE, irá ver que ficou assim:
Estrutura de pastas MVC
Isso é o correto, se a sua estrutura não ficou assim, tem algo errado.
Implementando a VO
Vamos utilizar duas classes desse tipo, uma chamada Passos que conterá as informações relacionadas à uma verificação pelo método de ordenação. A outra classe se chama Ordenação e contém os dados relativo ao resultado final do processo. Se não entendeu muito ainda, não tem problema, quando estiver implementado e funcionando você endentenderá.
package br.com.gqferreira.model.vo; /** * * @author Gustavo Ferreira * @see Classe que armazenas os dados relativo a cada passo da ordenacao */ public class Passos { private String numeroAnterior; private String numeroResultante; private String descricao; /** * Construtor que preenche todos os atributos do objeto * @param numeroAnterior * @param numeroResultante * @param descricao */ public Passos(String numeroAnterior, String numeroResultante, String descricao) { this.numeroAnterior = numeroAnterior; this.numeroResultante = numeroResultante; this.descricao = descricao; } //Get and Set public String getDescricao() { return descricao; } public void setDescricao(String descricao) { this.descricao = descricao; } public String getNumeroAnterior() { return numeroAnterior; } public void setNumeroAnterior(String numeroAnterior) { this.numeroAnterior = numeroAnterior; } public String getNumeroResultante() { return numeroResultante; } public void setNumeroResultante(String numeroResultante) { this.numeroResultante = numeroResultante; } //Sobrescrevendo o toString do objeto @Override public String toString() { if (this.getNumeroAnterior() == null) { return "\nDescricao: ".concat(this.descricao); } else { return this.getNumeroAnterior().concat(" >> ").concat(this.getNumeroResultante()).concat("\nDescricao: ").concat(this.descricao).concat("\n\n"); } } }
Segue a baixo a outra classe de valor
package br.com.gqferreira.model.vo; /** * * @author Gustavo Ferreira * @see Classe que cria objeto capaz de armazenar os dados relativo ao processo * de ordenacao */ public class Ordenacao { private int numeroOriginal; private String numeroOrdenado; private int qtdeTrocas; /** * Construtor padrao que insere em todos os atributos do objeto * @param numeroOriginal * @param numeroOrdenado * @param qtdeTrocas */ public Ordenacao(int numeroOriginal, String numeroOrdenado, int qtdeTrocas) { this.numeroOriginal = numeroOriginal; this.numeroOrdenado = numeroOrdenado; this.qtdeTrocas = qtdeTrocas; } //Get and Set public String getNumeroOrdenado() { return numeroOrdenado; } public void setNumeroOrdenado(String numeroOrdenado) { this.numeroOrdenado = numeroOrdenado; } public int getNumeroOriginal() { return numeroOriginal; } public void setNumeroOriginal(int numeroOriginal) { this.numeroOriginal = numeroOriginal; } public int getQtdeTrocas() { return qtdeTrocas; } public void setQtdeTrocas(int qtdeTrocas) { this.qtdeTrocas = qtdeTrocas; } //Sobrescrevendo o toString do objeto @Override public String toString() { return String.valueOf(this.numeroOriginal) .concat(" virou:\n") .concat(String.valueOf(this.numeroOrdenado)) .concat("\nQtde de trocas: ") .concat(String.valueOf(this.qtdeTrocas)); } }
Implementando a DAO
Também teremos duas classes DAO's, pois o sistema manipulará dois arquivos de texto. Se o sistema manipulasse dez arquivos, seria dez classes, uma para cada arquivo com os métodos que forem nescessários (excluir, salvar, editar, atualizar, etc.). A mesma regra valeria se fosse para manipular tabelas do banco de dados, uma DAO para cada tabela.
package br.com.gqferreira.model.dao; import br.com.gqferreira.model.vo.Passos; import java.io.FileNotFoundException; import java.io.PrintWriter; import java.util.List; /** * * @author Gustavo Ferreira * @see Classe que executa as operacoes de IO (entrada e saida) do sistema com relacao * aos dados resultantes do passo a passo */ public class PassosDAO { /** * Metodo que recebe todos os passos (lista) e salva todos em um arquivo * @param passos * @throws FileNotFoundException */ public void salvarPassos(Listpassos) throws FileNotFoundException{ PrintWriter pw = new PrintWriter("passos.txt"); for (Passos p : passos){ pw.print(p); } pw.flush(); pw.close(); } }
Segue abaixo a outra classe DAO
package br.com.gqferreira.model.dao; import br.com.gqferreira.model.vo.Ordenacao; import java.io.FileNotFoundException; import java.io.PrintWriter; /** * * @author Gustavo Ferreira * @see Classe que executa as operacoes de IO (entrada e saida) do sistema com relacao * aos dados resultantes da ordenacao */ public class OrdenacaoDAO { /** * Metodo que salva em um arquivo de texto os dados do objeto de ordenacao * @param Ordenacao ordenacao * @throws FileNotFoundException */ public void salvar(Ordenacao ordenacao) throws FileNotFoundException{ PrintWriter pw = new PrintWriter("ordenacao.txt"); pw.print(ordenacao); pw.flush(); pw.close(); } }
Implementando a BO
Nosso sistema terá apenas uma BO com apenas um método, o de ordenação pelo algoritmo "Bolha". Sistemas comerciais geralmente tem várias BO's, uma para cada responsabilidade. Por exemplo, essa BO que vamos criar é responsável por ordenar números e poderia conter vários métodos, cada um utilizando um algoritmo diferente de ordenação.
Todo acesso à classes DAO's é feito através das classes BO's. É como se a BO dissesse:
"Ninguém vai à DAO senão por mim!"
Imagine que você tem um sistema com três DAO's, cada uma acessa uma tabela do banco de dados.
A sua aplicação precisa inserir dados em uma tabela e logo em seguida inserir nas outras duas
mas se der erro na inserção da última tabela, a ação toda precisa ser desfeita para garantir
que "Ou insere em todas ou em nenhuma". Para assegurar isso, será preciso usar transações que
deverão ser manipuladas pela BO. Ou seja, a BO solicita as ações para as três DAO's e ela mesmo
gerencia para poder desfazer se for nescessário. Por isso sempre chame DAO's a partir da BO!
A BO a seguir pode confundir bastante, principalmente se você ficar batendo cabeça para entender como funciona a lógica de ordenação. Se isso acontecer, pense o seguinte: É uma classe que contém um metodo responsável em receber um número (532135), ordena-lo (123355) e em seguida solicitar às duas DAO's que salvem o passo-a-passo e o resultado, só isso.
package br.com.gqferreira.model.bo; import br.com.gqferreira.model.dao.OrdenacaoDAO; import br.com.gqferreira.model.dao.PassosDAO; import br.com.gqferreira.model.vo.Ordenacao; import br.com.gqferreira.model.vo.Passos; import java.util.ArrayList; import java.util.List; /** * * @author Gustavo Ferreira * @see Classe que contem o(s) metodo(s) de ordenacao e eh capaz de processa-los */ public class OrdenacaoBO { /** * Metodo responsavel em fazer a ordenacao pelo algorito BubbleSort (metodo bolha) * @param Int numero * @return Ordenacao */ public Ordenacao bubbleSort(int numero) { try { //Transforma em String para fazer as trocas consideranco caracter por caracter //Converto o numero do tipo Int para String e depois gero um array de chars. char[] digitos = String.valueOf(numero).toCharArray(); //Nosso 'balde' intermediario entre as trocas, variavel auxiliar. char aux; //Outra auxiliar que serve para armazenar o numero antes da modificacao para //se criar o 'Passo' char[] antes; //Variavel que sera incrementada a cada troca para contar quantas trocas houve int qtdeTrocas = 0; //Vetor de passos para descrever todo o processo Listpassos = new ArrayList (); //Variavel que marca determina se houve trocas, usada para //interromper o processo quando ja nao houver mais numeros a serem //ordenados boolean continua=true; //Sera percorrido todos os numeros de acordo com o tamanho da sequencia for (int i = 0; i < digitos.length; i++) { if (!continua){ //Verificando se foram feitas trocas no ultimo ciclo, se nao foram, indica que ja esta ordenado break; //Interrompe o algoritmo } //Descrevendo o passo passos.add(new Passos(null, null, "Inicio da verificacao numero ".concat(String.valueOf(i)).concat("\n------------\n"))); continua=false; //Percorrendo cada numero com o seu proximo for (int j = 0; j < digitos.length - 1; j++) { if (digitos[j] > digitos[j + 1]) { //Esse numero eh maior que o proximo, troca! antes = new String(digitos).toCharArray(); aux = digitos[j]; digitos[j] = digitos[j + 1]; digitos[j + 1] = aux; //Incrementando a quantidade de trocas qtdeTrocas++; //Descrevendo o passo passos.add(new Passos(new String(antes), new String(digitos), "Trocou-se o digito ".concat(String.valueOf(digitos[j+1])).concat(" pelo ").concat(String.valueOf(digitos[j])))); continua=true; } else { passos.add(new Passos(new String(digitos), new String(digitos), "Nao houve troca pois o numero ".concat(String.valueOf(digitos[j])).concat(" ja eh menor/igual que ").concat(String.valueOf(digitos[j + 1])))); } } } //Persiste os resultados Ordenacao ordenacao = new Ordenacao(numero, new String(digitos), qtdeTrocas); new OrdenacaoDAO().salvar(ordenacao); new PassosDAO().salvarPassos(passos); //Retorno um objeto da classe Ordenacao informando os resultados. return ordenacao; } catch (Exception ex) { ex.printStackTrace(); return null; } } }
Pronto, sua aplicação já estará funcionando, mesmo que sem nenhuma interação com o usuário e nem interface gráfica, essa é a essência da cama model, ser o motor da aplicação!
Testando a model
Vamos testar nossa aplicação agora, para isso coloque a seguinte classe no pacote principal da aplicação (br.com.gqferreira):
package br.com.gqferreira; import br.com.gqferreira.model.bo.OrdenacaoBO; import br.com.gqferreira.model.vo.Ordenacao; /** * * @author Gustavo Ferreira */ public class MVC { public static void main(String[] args) { Ordenacao ordenacao = new OrdenacaoBO().bubbleSort(532135); System.out.println(ordenacao); //Invoco o toString() da classe Ordenacao } }
Essa classe contém o método main que é o método principal e será nescessário daqui pra frente.
Agora você já pode testar o sistema, embora não tenha ainda interface gráfica. Para interagir com a camada model, será necessário
fazer alterações no código do método main.
MVC com Java e desktop (parte 2) de Gustavo Ferreira é licenciado sob uma Licença Creative Commons Atribuição-CompartilhaIgual 3.0 Não Adaptada.
Esse artigo é dividido nas seguintes partes:
Comentarios (26) para MVC com Java e desktop (parte 2)
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
Obrigado, fico feliz que tenha gostado. Sinta-se a vontade para continuar navegando pelos artigos.
Agraços,
Gustavo Ferreira.
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
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...
Muito bom mesmo, agora ficou muito mais claro.
Valew Gustavo.
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
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 ?
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'
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.
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 ..
Aqui ainda é bom dia Diego rsrs..
Que bom que você tenha achado útil o artigo, fico contente.
Abraços!
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
E o controller precisa conhecer as duas interfaces!? e se uma terceira, quarta , quinta forem adicionadas?
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
Parabéns muito bom.
Para quem se interessar, aqui tem outra aplição desktop com netbeans: http://www.memoriacache.com.br/aplicacoes-desktop-com-netbeans-swing/
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
Muito boa a didática, direta ao ponto e demonstra de forma muito clara. parabéns.
Artigo excelente!! Já pesquisei bastante para obter um exemplo de MVC. De todos que venho pesquisando, considerei esse, o melhor!! Obrigado!! Um abraço!
Me ajudou muito. Ótimo artigo, parabéns!
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.
Muito bom e agradeço pela explicação, procurei bastante como fazer MVC e final achei e bem explícito
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.
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!
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!