Atendendo ao pedido, e para ficar mais claro o padrão MVC, resolvi colocar um exemplo simples de CRUD para Web no padrão MVC.
Estou seguindo a abordagem sugerida no livro Core Servlets e Javaserver Pages, de Marty Hall e Larry Brown, capítulo 15: como integrar servlets e JSP: a arquitetura Modelo Visão Controlador (MVC). Abaixo um link para o livro on-line (este link está em Inglês, tem o livro à venda em Português).
[url]http://www.pdf.coreservlets.com/MVC.pdf[/url]
A classe ServletCliente funciona como o controlador MVC.
Os JSP´s funcionam como a view.
Na parte do modelo, utilizo a classe RoteiroCliente. Nessa classe aplico o padrão Roteiro de Transação (Transaction Script) do livro Padrões de Arquitetura de Aplicações Corporativas, de Martin Fowler. Essa é a classe em que coloco as regras de negócio. Neste exemplo as únicas regras de negócio são: o cliente deve ter 18 anos de idade ou mais, e o seu nome não pode ser vazio. Cito abaixo a página 72 do livro do Martin Fowler, que fala sobre a possibilidade de usar Roteiros de Transação como modelo:
Martin Fowler - Padroes de Arquitetura de Aplicações Corporativas:
A primeira e mais importante razão para aplicar o Modelo Vista Controlador é assegurar que os modelos estejam completamente separados da apresentação Web. (...) Colocar o processamento em objetos Roteiro de Transação ou Modelo de Domínio também tornará mais fácil testá-los. (...)
Poderia também utilizar uma classe de domínio como modelo - isso seria o padrão Modelo de Domínio citado acima (e o DDD - Domain-Driven Design). Porém o Martin Fowler recomenda utilizar um Mapeador de Dados (para o mapeamento objeto-relacional) ao utilizar um Modelo de Domínio mais rico, ou um Registro Ativo (padrão em que o modelo e o acesso a dados ficam na mesma classe) para domínios simples. Como eu não quis entrar na complexidade do mapeamento objeto-relacional neste exemplo, e queria separar o acesso a dados das regras de negócio, decidi utilizar o Roteiro de Transação.
A classe ClienteTO funciona como um Transfer Objetc, ou Objeto de Transferência de Dados. Esta classe serve apenas para transportar dados entre as camadas - não é propriamente uma classe de negócios.
E utilizo a classe ClienteDAO.java para interagir com o banco de dados. Ela é um Data Access Object. Para o exemplo funcionar, não coloquei um acesso real a banco de dados. Criei uma List na classe para armazenar os dados. Num caso real eu iria colocar o SQL nesta classe. Pode parecer que a classe ClienteDAO tem métodos muito parecidos com a classe RoteiroCliente. Isso acontece porque trata-se de um CRUD simples. A classe RoteiroCliente encapsula a regra de negócio, enquanto ClienteDAO encapsula o acesso a banco de dados. Num caso muito simples, é possível que sejam apenas uma classe - isso seria o padrão Registro Ativo, do livro Arquitetura de Aplicações Corporativas do Martin Fowler, pág. 165.
Segundo o Martin Fowler o valor do MVC está em duas separações:
1 - separar a apresentação do modelo (conforme trecho citado acima)
2 - separar a vista do controlador.
Cito abaixo um trecho em que ele fala sobre a segunda separação (pág. 317):
Martin Fowler - Padroes de Arquitetura de Aplicações Corporativas:
A separação entre a vista e o controlador é menos importante, de modo que eu só a recomendaria quando fosse realmente útil. Para sistemas com clientes ricos, isso acaba sendo quase nunca, embora seja comum em front ends Web no qual o controle é separado. A maioria dos padrões sobre o projeto Web é baseada nesse princípio.
Por isso eu não acho muito útil o MVC para aplicações Java desktop na maioria dos casos. Porém acho importante a primeira separação: separar a apresentação do modelo - para esse tipo de aplicação.
Abaixo o código do exemplo:
index.jsp
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>JSP Page</title>
</head>
<body>
<h1>Aplicativo Exemplo CRUD Clientes</h1>
<p> Regra de Negócio: um cliente tem que possuir no mínimo 18 anos. </p>
<a href = "ServletCliente?acao=listar">Entrar</a>
</body>
</html>
listaClientes.jsp
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@page import="to.ClienteTO"%>
<jsp:useBean id="lista"
scope="request"
class="java.util.List<to.ClienteTO>"/>
<jsp:useBean id="erro"
scope="request"
class="java.lang.String"/>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Lista</title>
</head>
<body>
<h2>Cadastro de Clientes</h2>
<a href="ServletCliente?acao=iniciarInclusao">
Novo Cliente
</a>
<a href="index.jsp">
Início
</a>
<table border="1" width="70%">
<tr>
<b>
<td>Nome</td>
<td>Idade</td>
<td>Opções</td>
</b>
</tr>
<% for(ClienteTO c:lista) { %>
<tr>
<td><%=c.getNome()%></td>
<td><%=c.getIdade()%></td>
<td>
<a href="ServletCliente?acao=iniciarAlteracao&codigo=<%=c.getCodigo()%>">
Alterar
</a>
<a href="ServletCliente?acao=excluir&codigo=<%=c.getCodigo()%>">
Excluir
</a>
</td>
</tr>
<% } %>
</table>
<font color="#FF0000">
<% if(erro != null) { %>
<%=erro%>
<% } %>
</font>
</body>
</html>
cliente.jsp
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@page import="to.ClienteTO"%>
<jsp:useBean id="dado"
scope="request"
class="to.ClienteTO"/>
<jsp:useBean id="erro"
scope="request"
class="java.lang.String"/>
<%!
String blanknullStr(String s) {
return (s==null) ? "" : s;
}
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>JSP Page</title>
</head>
<body>
<h2>Cadastro de Clientes</h2>
<form action="ServletCliente" method="post">
<input type="hidden" name="acao"
value="<%=(dado.getCodigo()==0)?"incluir":"alterar"%>">
<input type="hidden" name="codigo"
value="<%=dado.getCodigo()%>">
Nome*:
<input type="text" name="nome"
value="<%=blanknullStr(dado.getNome())%>">
<br>
Idade*:
<input type="text" name="idade"
value="<%=dado.getIdade()%>">
<br>
<input type="submit" value="Enviar">
</form>
<font color="#FF0000">
<% if(erro != null) { %>
<%=erro%>
<% } %>
</font>
</body>
</html>
ServletCliente.java
package servlet;
import java.io.IOException;
import java.util.List;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import roteiro.RoteiroCliente;
import to.ClienteTO;
/**
*
* @author calazans
*/
@WebServlet(name = "ServletCliente", urlPatterns = {"/ServletCliente"})
public class ServletCliente extends HttpServlet {
RoteiroCliente roteiro = new RoteiroCliente();
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String acao = request.getParameter("acao");
if(acao == null) acao = "listar";
if(acao.equals("listar")){
listar(request, response);
} else if(acao.equals("iniciarInclusao")){
iniciarInclusao(request, response);
} else if(acao.equals("iniciarAlteracao")){
iniciarAlteracao(request, response);
} else if(acao.equals("incluir")){
incluir(request, response);
} else if(acao.equals("alterar")){
alterar(request, response);
} else if(acao.equals("excluir")){
excluir(request, response);
} else {
listar(request, response);
}
}
private void listar(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
List<ClienteTO> lista = roteiro.carregarTodos();
request.setAttribute("lista", lista);
despacha(request, response, "listaClientes.jsp");
}
private void iniciarInclusao(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
despacha(request, response, "cliente.jsp");
}
private void iniciarAlteracao(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
int codigo = Integer.parseInt(request.getParameter("codigo"));
request.setAttribute("dado", roteiro.carregarPorCodigo(codigo));
despacha(request, response, "cliente.jsp");
}
private void incluir(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ClienteTO cliente = new ClienteTO();
try{
String nome = request.getParameter("nome");
int idade = parseIdade(request.getParameter("idade"));
cliente = new ClienteTO(0, nome, idade );
roteiro.incluir(cliente);
listar(request,response);
}catch(Exception e){
request.setAttribute("dado", cliente);
request.setAttribute("erro", "Ocorreu o seguinte erro: " + e.getMessage());
despacha(request, response, "cliente.jsp");
}
}
private void alterar(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ClienteTO cliente = new ClienteTO();
try{
int codigo = Integer.parseInt(request.getParameter("codigo"));
String nome = request.getParameter("nome");
int idade = parseIdade(request.getParameter("idade"));
cliente = new ClienteTO(codigo, nome, idade );
roteiro.alterar(cliente);
listar(request,response);
}catch(Exception e){
request.setAttribute("dado", cliente);
request.setAttribute("erro", "Ocorreu o seguinte erro: " + e.getMessage());
despacha(request, response, "cliente.jsp");
}
}
private void excluir(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
int codigo = Integer.parseInt(request.getParameter("codigo"));
try{
roteiro.excluir(codigo);
listar(request,response);
}catch(Exception e){
request.setAttribute("erro", "Ocorreu o seguinte erro: " + e.getMessage());
List<ClienteTO> lista = roteiro.carregarTodos();
request.setAttribute("lista", lista);
despacha(request, response, "listaClientes.jsp");
}
}
private int parseIdade(String idade) throws Exception{
try{
return Integer.parseInt(idade);
}catch(Exception e){
throw new Exception("Campo idade deve ser um valor numerico");
}
}
private void despacha(HttpServletRequest request, HttpServletResponse response, String pagina)
throws ServletException, IOException {
RequestDispatcher rd = request.getRequestDispatcher(pagina);
rd.forward(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
public String getServletInfo() {
return "Servlet para Manter Clientes";
}
}
RoteiroCliente.java
package roteiro;
import dao.ClienteDAO;
import java.util.List;
import to.ClienteTO;
/**
*
* @author calazans
* Roteiro de Transação de Cliente
*/
public class RoteiroCliente {
private ClienteDAO clienteDAO = new ClienteDAO();
public void incluir(ClienteTO cliente) throws Exception {
validar(cliente);
clienteDAO.incluir(cliente);
}
public void alterar(ClienteTO cliente) throws Exception{
validar(cliente);
clienteDAO.alterar(cliente);
}
public void excluir(int codigo) throws Exception{
clienteDAO.excluir(codigo);
}
public ClienteTO carregarPorCodigo(int codigo){
return clienteDAO.carregarPorCodigo(codigo);
}
public List<ClienteTO> carregarTodos(){
return clienteDAO.carregarTodos();
}
private void validar(ClienteTO cliente) throws Exception{
if(cliente.getIdade() < 18){
throw new Exception("Idade invalida.");
}
if(cliente.getNome() == null || cliente.getNome().equals("")){
throw new Exception("Nome invalido.");
}
}
}
ClienteTO.java
package to;
/**
*
* @author calazans
* Transfer Object de Cliente
*/
public class ClienteTO {
private int codigo;
private String nome;
private int idade;
public ClienteTO(){
codigo = 0;
nome = "";
idade = 0;
}
public ClienteTO(int codigo, String nome, int idade) {
this.codigo = codigo;
this.nome = nome;
this.idade = idade;
}
public int getIdade() {
return idade;
}
public String getNome() {
return nome;
}
public int getCodigo() {
return codigo;
}
public void setCodigo(int codigo) {
this.codigo = codigo;
}
@Override
public boolean equals(Object obj) {
ClienteTO c = (ClienteTO) obj;
if(c.getCodigo()==this.getCodigo()) return true;
else return false;
}
}
ClienteDAO.java
package dao;
import java.util.ArrayList;
import java.util.List;
import to.ClienteTO;
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
/**
*
* @author calazans
* Data Access Object de Cliente
*/
public class ClienteDAO {
private List<ClienteTO> listaClientes = new ArrayList();
public ClienteDAO(){
listaClientes.add(new ClienteTO(1, "Francisco Paulo Souza", 31));
listaClientes.add(new ClienteTO(2, "Maria Joana Souza", 29));
listaClientes.add(new ClienteTO(1, "Paulo Franco Silva", 32));
}
public ClienteTO carregarPorCodigo(int codigo){
for(ClienteTO c:listaClientes){
if(c.getCodigo()==codigo) return c;
}
return null;
}
public List<ClienteTO> carregarTodos(){
return listaClientes;
}
public void incluir(ClienteTO cliente) {
cliente.setCodigo(novoCodigo());
listaClientes.add(cliente);
}
public void alterar(ClienteTO cliente) throws Exception{
for(int posicao = 0; posicao<listaClientes.size(); posicao++){
if(listaClientes.get(posicao).equals(cliente)){
listaClientes.set(posicao,cliente);
return;
}
}
throw new Exception("Cliente nao localizado.");
}
public void excluir(int codigo) throws Exception{
for(ClienteTO c:listaClientes){
if(c.getCodigo() == codigo){
listaClientes.remove(c);
return;
}
}
throw new Exception("Cliente nao localizado.");
}
private int novoCodigo(){
int maior = 0;
for(ClienteTO c:listaClientes){
maior = c.getCodigo() > maior ? c.getCodigo() : maior;
}
return ++maior;
}
}
Anexei um diagrama ilustrando essa arquitetura. O diagrama é apenas para mostrar a estrutura de camadas, não corresponde exatamente aos pacotes java.
Fiz esse projeto no NetBeans. Anexei o projeto.
Concluo dizendo que esta é uma forma de estruturar a arquitetura do sistema. Há várias outras formas possíveis. A arquitetura do sistema tem que ser sempre pensada de acordo com as características do sistema. No livro do Martin Fowler há várias abordagens de camadas, e explicações sobre quando utilizar uma ou outra abordagem. A quem se interessar, coloco o link:
[url]http://books.google.com.br/books?id=vpHqYZcmeKsC&printsec=frontcover&dq=martin+fowler+padr%C3%B5es+arquitetura&hl=pt-BR&sa=X&ei=NCbvT9bQLKPX6gHJ_NSXBg&ved=0CDYQ6AEwAA#v=onepage&q=martin%20fowler%20padr%C3%B5es%20arquitetura&f=false[/url]
