Criticas, Sugestoes, Dicas na minha Aplicação - Padrão MVC

Ola, a uns dias atras estudei sobre MVC mas não consigo ainda pensa como deixar minha Visão totalmente independente do meu modelo, mas mesmo assim resolvi começar a fazer meu aplicativo que na verdade ele vai servir pra controlar caixa, entrada e saida de merdadoria, estoque, clientes e fornecedores.

Vamos ao que eu já fiz (resumo)

DAO generico

[code]public class GenericDAO<T, PK extends Serializable> {
private Class<?> persistentClass;
private Session session;

public GenericDAO(){}

public GenericDAO(Class<?> persistentClass){
	this.setPersistentClass(persistentClass);
}

public GenericDAO(Class<?> persistentClass, Session session){
	this.setPersistentClass(persistentClass);
	this.session = session;
}

public void setPersistentClass(Class<?> persistentClass) {
	this.persistentClass = persistentClass;
}

public Class<?> getPersistentClass() {
	return persistentClass;
}

public void setSession(Session session){
	this.session = session; 
}

public Session getSession(){
	return session;
}

public T get(PK id){
	@SuppressWarnings("unchecked")
	T object = (T) getSession().get(getPersistentClass(), id);
	return object;
}

public void set(T obj, Transaction transaction){
	getSession().saveOrUpdate(obj);
	getSession().flush();
}

@SuppressWarnings("unchecked")
public List<T> getAll(){
	Criteria criteria = getSession().createCriteria(getPersistentClass());
	return criteria.list();
}

}[/code]

Interfase Controle (ainda incompleta)

public interface Controle<T> { public List<T> getAll(); }

Controle dos Clientes (ainda incompleto)

[code]public class ControleCliente implements Controle{
final GenericDAO<Cliente, Long> dao = new GenericDAO<Cliente, Long>(
Cliente.class, HibernateUtil.getSession());

public ControleCliente(){
}

public List<Cliente> getAll(){
	return dao.getAll();
}

public Double getAReceber(Cliente cliente){
	double aReceber = 0;
	for(Venda venda : cliente.getVendas()){
		for(Saida saida : venda.getSaidas()){
			aReceber += saida.getPesoSaco()*saida.getQuant()*saida.getPreco();
		}
		for(Recebimento rec : venda.getRecebimentos()){
			aReceber -= rec.getValor();
		}
	}
	return aReceber;
}

}[/code]

Enum com as colunas do TableModel

@SuppressWarnings("unchecked")
public enum TableColumnsVer implements Coluna<Cliente> {
	NOME {
		@Override
		public Class<?> getColumnClass() {
			return String.class;
		}

		@Override
		public String getColumnName() {
			return "Nome";
		}

		@Override
		public Object getValue(Cliente row) {
			return row.getNome();
		}
	},
	TEL_1 {
		@Override
		public Class<?> getColumnClass() {
			return String.class;
		}

		@Override
		public String getColumnName() {
			return "Telefone";
		}

		@Override
		public Object getValue(Cliente row) {
			return row.getTel_1();
		}
	},
	TEL_2 {
		@Override
		public Class<?> getColumnClass() {
			return String.class;
		}

		@Override
		public String getColumnName() {
			return "Telefone";
		}

		@Override
		public Object getValue(Cliente row) {
			return row.getTel_2();
		}
	},
	A_RECEBER {
		@Override
		public Class<?> getColumnClass() {
			return Double.class;
		}

		@Override
		public String getColumnName() {
			return "A receber";
		}

		@Override
		public Object getValue(Cliente row) {
			return getControle().getAReceber(row);
		}
	};

	public ControleCliente controleCliente;
	
	@Override
	public int getModelIndex() {
		return this.ordinal();
	}

	@Override
	public int getWidth() {
		return 50;
	}
	
	@Override
	public ControleCliente getControle(){
		if(controleCliente == null){
			controleCliente = new ControleCliente();
		}
		return controleCliente;
	}

}

Visão dos Clientes

[code]public class Ver extends JPanel {
private JScrollPane scrTbl;
private JTable tbl;
private JPanel pBts;
private JButton bVer;
private JButton bEditar;
private JButton bAdd;
private TableModel tblModel;

private ControleCliente controleCliente;
public Ver(){
	super();
	inicia();
}

private void inicia(){
	this.setLayout(new BorderLayout());
	this.add(getScrTbl(), BorderLayout.CENTER);
	this.add(getPBts(), BorderLayout.SOUTH);
}
private JPanel getPBts() {
	if(pBts == null){
		pBts = new JPanel();
		pBts.add(getBVer());
		pBts.add(getBEditar());
		pBts.add(getBAdd());
	}
	return pBts;
}

private JButton getBVer() {
	if(bVer == null){
		bVer = new JButton("Ver Vales");
	}
	return bVer;
}

private JButton getBEditar() {
	if(bEditar == null){
		bEditar = new JButton("Editar");
	}
	return bEditar;
}

private JButton getBAdd() {
	if(bAdd == null){
		bAdd = new JButton("Novo Cliente");
	}
	return bAdd;
}

private JScrollPane getScrTbl(){
	if (scrTbl == null) {
		scrTbl = new JScrollPane();
		scrTbl.setViewportView(getTbl());
	}
	return scrTbl;
}

private JTable getTbl() {
	if(tbl==null){
		tbl = new JTable();
		tbl.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		tbl.setModel(getTblModel());
		
	}
	return tbl;
}

private TableModel<Cliente> getTblModel() {
	if(tblModel==null){
		tblModel = new TableModel<Cliente>(getControle().getAll(), TableColumnsVer.values());
	
	}
	return tblModel;
}

private ControleCliente getControle(){
	if(controleCliente == null){
		controleCliente = new ControleCliente();
	}
	return controleCliente;
}

}[/code]

bom essas são algumas class da minha aplicação mas já da de vê como ta ficando minha estrutura

Mas oque eu gostaria de ter certeza é o seguinte

:arrow: O modelo ficam com as class de persistência (que representam as tabelas do banco), com os DAO e com o HiberneitUtil
:arrow: O controle e o responsável de por métodos como obterTodos() (mas não fazer a busca no banco, mas sim pedir ao DAO), abrir e fechar as sessões e as transações, fazer cálculos com os dados pegos dos DAOs (como receber o preço de cada produto e multiplicar pela quantidade) e tratar as acessões
:arrow: A visão fica responsável, somente por montar a interface gráfica tendo direito a somente acessar métodos do controle e exibir os eros pro usuário tratados pelo controle

Montei minha aplicação pensando nesses conceitos mas um eu não consegui cumprir o de usar somente métodos do controle na visão pois não consigo pensar em um jeito de no enum das colunas não usar métodos como getNome() que é da class cliente que fica no modelo.

Gostaria de saber se isso quebra o padrão MVC e se o que eu penso sobre MVC está coreto??

Até!

Não utilize os enums para seu table model, pelo jeito vc deve estar usando o auto-filtro do ViniGodoy aqui do forúm. Utilize o ObjectTableModel do projeto Towel do Mark (aqui do forúm tbm…) que inclusive já está com as classes do auto-filtro do ViniGodoy.

Link do projeto Towel (ObjectTableModel): http://www.guj.com.br/java/227402-markutils-sob-novo-nome/1

Sobre o MVC dá uma olhadinha nesse tutorial aqui http://www.oracle.com/technetwork/articles/javase/mvc-136693.html. É da propria oracle mostrando como aplicar o MVC com o PropertyChangeSupport da própria API.

Eu particularmente já ví vários outros tipos de aplicação do MVC, mas gostei muito desse.

Primeira coisa: a sua lógica do dominio esta vazando para o controle:

	public Double getAReceber(Cliente cliente){
		double aReceber = 0;
		for(Venda venda : cliente.getVendas()){
			for(Saida saida : venda.getSaidas()){
				aReceber += saida.getPesoSaco()*saida.getQuant()*saida.getPreco();
			}
			for(Recebimento rec : venda.getRecebimentos()){
				aReceber -= rec.getValor();
			}
		}
		return aReceber;
}

Para ter uma base de como funciona o MVC você tem que conhecer antes os padrões de projeto então leia Use a cabeça Padrões de Projeto
Para não ter objetos anêmicos evitar que a lógica do domínio se misture com a lógica de infraestrutura leia Domain Driven Design do Eric Evans

bom primeiro desculpa a demora para retornar

joyle não estou usando o alto filtro do ViniGodoy nem mesmo o ColumnModel (acho que é esse o nome) dele, esse é um TableModel que eu mesmo fiz, mas porque não se deve usar Enums??

Vou até tentar ler o link recomendado, mas é difícil o meu inglês é muito fraco.

Valeu!

x@ndy tu diz que a lógica do domínio deveria ficar no modelo?

mas o que é a logica de domínio, os cálculos feitos com os dados do banco??

não achei material online sobre “logica de domínio”, teria outro termo que signifique a mesma coisa??

Sobre os livros to sem grana pra comprar, mas pesquisando aqui na rede de bibliotecas da escola achei o Use a cabeça Padrões de Projeto, agora me resta saber o endereço dessa biblioteca!

Até

joyle tentando ler o link indicado fiquei com uma duvida quem deve estender AbstractModel as class de persistência ou os DAOs??

Pensei que fosse nas class de persistência mas me dei conta que alterar os atributos ali não altera os dados no banco, dai passei a achar que fosse nos DAOs mas os métodos deles se resumem a atualizar (que recebe uma class de persistência como parâmetro), adicionar (que recebe uma class de persistência como parâmetro), obter (que recebe um long como parâmetro) e obterTodos(sem parâmetros), qual seria a saída??

[edit]Agora me passou pela cabeça em colocar nas class de persistências e os métodos setter dela chamarem o DAO, acho que até funcionaria mas isso não estaria totalmente fora de padrão(gambiarra)??[/edit]

Até!

[quote=InSeOfKn]

x@ndy tu diz que a lógica do domínio deveria ficar no modelo?

mas o que é a logica de domínio, os cálculos feitos com os dados do banco??

não achei material online sobre “logica de domínio”, teria outro termo que signifique a mesma coisa??

Sobre os livros to sem grana pra comprar, mas pesquisando aqui na rede de bibliotecas da escola achei o Use a cabeça Padrões de Projeto, agora me resta saber o endereço dessa biblioteca!

Até[/quote]

Lógica de domínio são as regras de negócio do seu sistema. Imagine um jogo 3D. Todo jogo tem regras, certo? Bom, as regras do jogo são parte da lógica de domínio.

Também gostaria de saber. Eu mesmo tenho um TableModel onde as colunas são descritas por enums, como vc fez. Na verdade, ele é descrito por uma lista de uma interface chamada Column, e o enum é só uma maneira conveniente de implementar essa lista (como vc fez tb).

E PS: É auto-filtro. O filtro é automático, não grande. :slight_smile:

Desculpe, na verdade queria dizer que você deve evitar nesse caso! Note, se vc tem suas tabelas com mais de 30 campos por exemplo, imagina escrever enums com mais de 30 campos para sua tabela! Disse isso por que isso é só um pouco chato e cansativo :wink:
Os enums resolvem essa situação de forma elegante, mas o modelo que o Mark propõe é bem melhor que ficar escrevendo enums. :wink:

De uma olhada no projeto Towel no link que te passei anteriormente.

[quote=InSeOfKn]joyle tentando ler o link indicado fiquei com uma duvida quem deve estender AbstractModel as class de persistência ou os DAOs??

Pensei que fosse nas class de persistência mas me dei conta que alterar os atributos ali não altera os dados no banco, dai passei a achar que fosse nos DAOs mas os métodos deles se resumem a atualizar (que recebe uma class de persistência como parâmetro), adicionar (que recebe uma class de persistência como parâmetro), obter (que recebe um long como parâmetro) e obterTodos(sem parâmetros), qual seria a saída??

[edit]Agora me passou pela cabeça em colocar nas class de persistências e os métodos setter dela chamarem o DAO, acho que até funcionaria mas isso não estaria totalmente fora de padrão(gambiarra)??[/edit]

Até![/quote]

Seus models devem estender o AbstractModel! No AbstractModel existe um método chamado firePropertyChange(String propertyName, Object oldValue, Object newValue) que deve chamado pelo seu model toda vez que ocorre uma mudança nele e isso interfere na renderização do seu model, então é necessário notificar a view.

No meu caso, eu deixo o controller responsável por chamar o DAO e passar o objeto a ser persistido.

Acredito que sim. Se vc fizer isso, toda vez que vc alterar seu atributo, vc terá que persistir seu model, o que é inviável quando não há a real necessidade disso. Persista somente quando necessário.

Ops… :lol: . Consertei lá em cima.

Acho interessante falar também do Binder do projeto Towel, ele é bem interessante de se trabalhar e evita escrever um monte de propriedades como o PropertyChangeSupporte propõe

Dá uma olhada aqui nesse link : http://markytechs.wordpress.com/2010/03/03/binder-2-0-agora-com-annotations/

Você atualiza suas views e seus models de uma maneira bem mais fácil e elegante também.

Ainda estou testando e é bem legal mesmo.

Ta, mas eu estenderia AbstractModel em todas as class que pertence ao modelo, entre elas as class de persistência (as que são anotadas com @Entity), os DAOs até mesmo o HibernateUtil??
acredito que não pois nem todas notificam mudanças, na verdade a únicas que modificam o banco são os DAOs mas eles não sabem que propriedade foi modificada, já que as propriedades estão das class de persistência a que o DAO já recebe atualizada mas ainda não persistida

Até

[quote=InSeOfKn][quote=joyle]
Seus models devem estender o AbstractModel! No AbstractModel existe um método chamado firePropertyChange(String propertyName, Object oldValue, Object newValue) que deve chamado pelo seu model toda vez que ocorre uma mudança nele e isso interfere na renderização do seu model, então é necessário notificar a view.
[/quote]
Ta, mas eu estenderia AbstractModel em todas as class que pertence ao modelo, entre elas as class de persistência (as que são anotadas com @Entity), os DAOs até mesmo o HibernateUtil??
acredito que não pois nem todas notificam mudanças, na verdade a únicas que modificam o banco são os DAOs mas eles não sabem que propriedade foi modificada, já que as propriedades estão das class de persistência a que o DAO já recebe atualizada mas ainda não persistida

Até[/quote]

Não, somente os seus models é que vão estender do AbstractModel. Somente o model deve notificar a view de mudanças.
Os DAOs não sabem e nem devem saber se o model foi modificado ou não, eles são apenas para persistir os dados e é você que define quando eles irão persistir.

Posso estar enganado, mas acredito que você está com um pouco de dificuldades para aplicar o padrão. Dê uma lidinha a mais no tutorial, lá tem também o código fonte contendo todo o exemplo, baixe-o e teste. Há outros links sobre MVC lá também dê uma lida.

Mas o que seria o Model? não seria o M do (MVC)? se for os DAOs não fazem parte de dele?

Até!

[quote=InSeOfKn]Mas o que seria o Model? não seria o M do (MVC)? se for os DAOs não fazem parte de dele?

Até![/quote]

O model é a sua classe de negócio, que também pode ser mapeada para ser relacionada com o banco.

Você está utilizando algum framework como Hibernate por exemplo? As classes POJOS do hibernate é que eu extendo do AbstractModel e quando executo algum set ou algum outro método que mude o estado do objeto eu chamo o firePropertyChange.

Deixa eu ver se entendi, O Model é as Class de persistência (não sei esse o nome são as class anotadas com @Entity) junto com os métodos de negocio como aReceber() entre outros

O DAO não faz parte do Model

É isso???

Dá uma olhda nesse link aqui também e vê se te ajuda mais.

http://wiki.sintectus.com/bin/view/GrupoJava/MVC

[quote=InSeOfKn]Deixa eu ver se entendi, O Model é as Class de persistência (não sei esse o nome são as class anotadas com @Entity) junto com os métodos de negocio como aReceber() entre outros

O DAO não faz parte do Model

É isso???
[/quote]

Isso. O DAO não faz parte do model não, ele é apenas para persistir o model com o banco.

Esse aqui é um exemplo do DAO que utilizo para gravar os bancos por exemplo.

[code]/*
package com.jns.bancos;

import com.jns.util.AbstractDAO;
import com.jns.util.AbstractModel;
import com.jns.util.CrudView;

public class BancosDAO extends AbstractDAO {

public BancosDAO(CrudView view) {
    this.view = view;
}

public void recordSave(AbstractModel bancoObject) {
    if (bancoObject instanceof BancosModel) {
        try {
            sessionOpen();
            transactionBegin();

            getSession().save(bancoObject);

            transactionCommit();
            sessionClose();
        } catch (Exception e) {                
        }
    } else {
        throw new IllegalArgumentException("O argumento não é uma " + BancosModel.class);
    }
}

public void recordDelete(AbstractModel bancoObject) {
    if (bancoObject instanceof BancosModel){
        try {
            sessionOpen();
            transactionBegin();

            getSession().delete(bancoObject);

            transactionCommit();
            sessionClose();
        } catch (Exception e) {                
        }
    } else {
        throw new IllegalArgumentException("O argumento não é uma " + BancosModel.class);
    }
}

public void recordUpdate(AbstractModel bancoObject) {
    if (bancoObject instanceof BancosModel){
        try {
            sessionOpen();
            transactionBegin();

            getSession().saveOrUpdate(bancoObject);

            transactionCommit();
            sessionClose();
        } catch (Exception e) {
        }
    } else {
        throw new IllegalArgumentException("O argumento não é uma " + BancosModel.class);
    }
}

public boolean recordExists(AbstractModel object) {
    throw new UnsupportedOperationException("Not supported yet.");
}

}[/code]

Eu estou usando o Hibernate você teria um exemplo de uma class POJO(que té então estava chamando de class de persistência) para min ver como fica ele estendendo AbstractModel??

[quote=InSeOfKn]Eu estou usando o Hibernate você teria um exemplo de uma class POJO(que té então estava chamando de class de persistência) para min ver como fica ele estendendo AbstractModel??

[/quote]

Claro. Tá aí um protótipo.

[code]import com.jns.util.AbstractModel;
import com.towel.el.annotation.Resolvable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name=“bancos”)
public class BancosModel extends AbstractModel implements java.io.Serializable {

@Resolvable(colName="ID Banco:")
private Integer bancoId;

@Resolvable(colName="Cód. Banco:")
private String bancoCodBanco;

@Resolvable(colName="Nome Banco:")
private String bancoNome;

@Resolvable(colName="Web Site:")
private String bancoWebsite;

public BancosModel() {
}


public BancosModel(int bancoId, String bancoCodBanco, String bancoNome) {
    this.bancoId = bancoId;
    this.bancoCodBanco = bancoCodBanco;
    this.bancoNome = bancoNome;
}
public BancosModel(int bancoId, String bancoCodBanco, String bancoNome, String bancoWebsite) {
   this.bancoId = bancoId;
   this.bancoCodBanco = bancoCodBanco;
   this.bancoNome = bancoNome;
   this.bancoWebsite = bancoWebsite;
}

@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="banco_id", unique=true, nullable=false)
public Integer getBancoId() {
    return this.bancoId;
}

public void setBancoId(Integer bancoId) {
    Integer oldId = this.bancoId;
    this.bancoId = bancoId;
    firePropertyChange(BancosController.BANCO_ID_PROPERTY, oldId, bancoId);
}

@Column(name="banco_cod_banco", nullable=false, length=5)
public String getBancoCodBanco() {
    return this.bancoCodBanco;
}

public void setBancoCodBanco(String bancoCodBanco) {
    String oldCodBanco = this.bancoCodBanco;
    this.bancoCodBanco = bancoCodBanco;
    firePropertyChange(BancosController.BANCO_COD_PROPERTY, oldCodBanco, bancoCodBanco);
}

@Column(name="banco_nome", nullable=false)
public String getBancoNome() {
    return this.bancoNome;
}

public void setBancoNome(String bancoNome) {
    String oldBancoNome = this.bancoNome;
    this.bancoNome = bancoNome;
    firePropertyChange(BancosController.BANCO_NOME_PROPERTY, oldBancoNome, bancoNome);
}

@Column(name="banco_website")
public String getBancoWebsite() {
    return this.bancoWebsite;
}

public void setBancoWebsite(String bancoWebsite) {
    String oldBancoWebSite = this.bancoWebsite;
    this.bancoWebsite = bancoWebsite;
    firePropertyChange(BancosController.BANCO_WEBSITE_PROPERTY, oldBancoWebSite, bancoWebsite);
}

}[/code]

Mas antes de sair fazendo um monte de POJOs por aí veja esse link abaixo.
http://blog.caelum.com.br/nao-aprender-oo-getters-e-setters/