Por favor analizem essa aplicação e comentem

Ola pessoal em minha peregrinação rumo ao MVC fiz esse sistema de teste.

Descrição do sistema :
Na verdade é um sistema bem simples existem dois JTextField no qual o usuario digita valores numericos e depois pode clicar em somar ou subtrair. o resultado aparece em um terceiro JTextField.

O Sistema é dividido em 6 arquivos :

1)Calc.java = Seria nossa regra de negocios.
2)Command.java = A Interface Command
3)CommandSomar.java = O comando responsavel por adicionar
4)CommandSubtrair.java = O comando responsavel por subtrair
5)JFrameCalc.java = Estende javax.swing.JFrame é view da aplicação
Nesse arquivo existe uma classe interna que seria uma especie de controller responsavel por executar os comandos.
6)MainCalc.java = Classe que contem o metodo main da aplicação :

agora segue os códigos :

package command.dois;

import java.util.*;

public class Calc extends Observable {
	
	private int ultValue=0;
	
	public void somar(int a, int b) {
		setUtlValue(a + b);
	}
	
	public void subtrair(int a, int b) {
		setUtlValue(a - b);
	}
	
	private void setUtlValue(int newValue) {
		ultValue = newValue;
		setChanged();
		notifyObservers(this);
		
	}
	public int getUtlValue() {
		return ultValue;
	}
}
package command.dois;

public interface Command {
	public void execute(int a, int b);
}
package command.dois;

public class CommandSomar implements Command {
	
	private Calc calc;
	
	public CommandSomar(Calc calc) {
		this.calc = calc;
	}
	
	public void execute(int a, int b) {		
		calc.somar(a,b);		
	}

}
package command.dois;

public class CommandSubtrair implements Command {
	
	private Calc calc;
	
	public CommandSubtrair(Calc calc) {
		this.calc = calc;
	}
	
	public void execute(int a, int b) {
		calc.subtrair(a,b);		
	}

}
package command.dois;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.FlowLayout;

import java.awt.event.*;
import javax.swing.*;
import java.util.*;

public class JFrameCalc extends JFrame implements Observer {
	
	private JButton btSomar,btSubtrair;
	private JTextField txUm,txDois,txResultado;
	
	public JFrameCalc() {
		
		super("Calculadora ");
		buildInterface();
		
	}
	
	private void buildInterface(){
		Calc calc = new Calc();
		calc.addObserver(this);
		
		Container container = getContentPane();
		
		btSomar    = new JButton("Somar");
		btSubtrair = new JButton("Subtrair");
		
		Event evSomar = new Event(new CommandSomar(calc));
		
		Event evSubtrair = new Event(new CommandSubtrair(calc));
		
		btSomar.addActionListener(evSomar);
		btSubtrair.addActionListener(evSubtrair);
				
		txUm = new JTextField("",5);
		txDois = new JTextField("",5);
		txResultado = new JTextField("0",5);
		txResultado.setEditable(false);
		
		container.add(new JLabel("Cacl"),BorderLayout.NORTH);
		
		JPanel panel = new JPanel();
		panel.setLayout(new FlowLayout());
		
		panel.add(new JLabel("Um"));
		panel.add(txUm);
		panel.add(new JLabel("Dois"));
		panel.add(txDois);
		panel.add(txResultado);
		
		panel.add(btSomar);
		panel.add(btSubtrair);
		
		container.add(panel,BorderLayout.CENTER);	
				
		setSize(200,200);
		setVisible(true);
	}
	class Event implements ActionListener {
		

		
		private Command command;
		
		public Event( Command command) {
			this.command = command;
		}
		
		public void actionPerformed(ActionEvent e) {
			command.execute(
				Integer.parseInt(txUm.getText()),
				Integer.parseInt(txDois.getText()));							
		}
		
	}
	
	public void update(Observable o, Object arg) {
		Calc calc = (Calc)arg;
		txResultado.setText(calc.getUtlValue()+"");		
	}
	
}
package command.dois;
import javax.swing.*;
public class MainCalc {
	public static void main(String args[]) {
		JFrameCalc application = new JFrameCalc();
		application.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
}

vamos agora algumas questões :

Primeiro a classe Calc implementa a interface Observable pois ele será o model e a classe JFrameCalc implementa a interface Observer pois esta classe será view… tudo até ae certo , né ?

O Grande problema vem na construção do Controller no caso acima eu criei uma classe interna Event que recebe um Command como Argumento.eu crio um Event para cada botão . tipo para somar eu crio um Evento que recebe o CommandSomar como argumento e assim por diante .

o que acham dessa estrutura ?
como posso torna-la melhor ?

falo isso porque apesar que essa estrutura funcionar razoavelmente bem neste exemplo. Mas se o sistema fosse maior ? Com 200 View… Teria sei la 500 Commands (por exemplo) ?

Outra questão é quando eu tenho um sistema que tem 50 Regras de negocios eu vou ter que ter 50 views e o PIOR cada view tera que ter sua propria regra de negocio ?

Baixe os fontes clicando abaixo.
http://ualex.sosphp.com/Command.zip

Obrigado pessoal. Qualquer ajuda sera bem vinda.

[quote=ualex]Primeiro a classe Calc implementa a interface Observable pois ele será o model e a classe JFrameCalc implementa a interface Observer pois esta classe será view… tudo até ae certo , né ?
[/quote]

mais ou menos… porque seu método recebe um Observable e um Object, e trata o Object?

Eu não vejo porque um observador ter mais de um assunto enste seu sisteminha, mas ainda que estivesse (‘ah, estou usando o framework XYZ que tem esses trecos…’), o assunto não seria o seu Observable? Por que o Object?

Uhm…

Na verdade, evite classes internas. Elas complicam que só, mas se calhar de usar, acho que seu exemplo não está muito legal…

O controlador precisa receber um estímulo, pode ser um Event, mas eu achava legal ser algo desacoplado do Swing/AWT, como uma String de mensagem ou a própria Action, achar a Action a ser executada (seja porque ele a recebeu ou porque ele sabe que baseado na String/Event recebido ele deve executar a action XYZ) e a executar.

Peraí… você não terá uma acção por botão, frame…sei lá. Você terá uma ação por cada interação que seu usuário faz, basicamente você terá uma ou algumas ações por caso de uso. Se voce pode incluir um usuário em mil lugares, você vai ter uma ação sendo chamada mil vezes. Pense bem…você não tem tantas ações assim.

‘50 regras de negócio’…? Uhm… complicado isso. Você está confundindo um pouco os conceitos…

‘Regras de negócio’ são o conjunto de restrições impostas pelos requisitos de um sistema. Uma regra de negócio é que meu usuário tem que ter nascido entre 24/10/1982 e 21/02/1983, por exemplo… se eu sou seu cliente as regras de negócio são o conjunto de coisas que eu mando o sistema que você construir obedecer.

Você não rpecisa ter um formulário para cada requisito/caso de uso/história/sei lá o que você usar. Você precisa ter formulários que possibilitem ao seu usuário realizar as ações planejadas, não mais não menos. Se você pode num mesmo formulário exibir a lista de usuários, permitir inclusão, exclusão e alteração sem comprometer seus requisitos nem o bom-senso (nada de telas poluídas em excesso!!), ótimo, use uma tela só!

Não pdue fazer isso…estou no McDonald’s agora :smiley:

[]s

Então eu utilizo o segundo argumento(Object arg) , porque eu não consigo pegar as informações que eu preciso do objeto Observable. Já com Object arg eu faço um cast (acho que downcast)promovo(ou rebaixo) ele a minha regra de negocio… Isso ta errado ?

outra coisa eu to estudando Xwork e pendulum… Mas eu tou apanhando. Mas acho se um dia eu conseguir entender vai facilitar bastante fazer um sistema em camadas utilizando o swing.

Não entendo aquele xwork.xml dentre outras coisa… Mas muito obrigado por responder…

[quote=ualex][quote]
public void update(Observable o, Object arg) {

}
[/quote]
Então eu utilizo o segundo argumento(Object arg) , porque eu não consigo pegar as informações que eu preciso do objeto Observable. Já com Object arg eu faço um cast (acho que downcast)promovo(ou rebaixo) ele a minha regra de negocio… Isso ta errado ?[/quote]

O que você passa neste método?

Um observador tem que receber a notificação do seu assunto, então você deve passar apenas o objeto observado :wink:

Dê uma olhada no pattern e você vai ter uma idéia sobre isso meslhor…

[]s

então o segundo argumento é objeto observado. o Primeiro é um Observable ou seja que extende a class java.util.Observable e portanto só sera visivel para mim os metodos definidos nessa classe. Por isso no model eu faço isso:

setChanged();
notifyObservers(this);

esse this vai como segundo argumento. //Object

tipo essa implementação do design pattern Observable/Observer é padrão da sun(java.util).

tipo acho que isso ta certo.

Não é porque a Sun faz alguma coisa de um jeito X que você deve considerar como certo. O Design Pattern da GoF Observer define que um Observador recebe como argumento um assunto (observado), se você achar um bom motivo para mudar isso ok, mas se não…

A interface da sun provê um parâmetro a ser passado mas não explica para quê alguém passaria algo além do assunto. Para quê você precisa dele? Isso me cheira a um problema de acoplamento de controle…

[]s

preciso dele para pegar valores do model.No caso o resultado da operação. Acho que o ideial era implementar uma interface que seria só para transmitir dados do model para view de maneira unica

O método update não serve para passar valores, mas para dizer que um assunto foi atualizado :wink:

[]s

O metodo update é invocado toda vez que o Observable notifica os seus Observer(s). Assinatura do metodo é Observable que esta enviando a notificação e um Object arg que imagino eu que possa onde vc enviarei informações mas detalhadas sobre as mudanças que ocorreram.

tipo se isso não é melhor qual é a melhor forma.

Um observador deve saber como extrair a informação do que ele observa. Não consigo pensar em um motivo para passar um parâmetro… ma s a sun deve usar isso para alguma coisa.

Eu acho, baseado no que vc disse, que vc está usando Observer para passar informações entre classes. Isto não é legal, não é o objetovo do pattern. para passar informações entre classes, simplesmente pchame um método e passe parâmetros, use Observer para notificar um objeto de que outro foi alterado :wink:

[]s

como assim?

Eu acho que deveria existir um objeto chamado DADOS.

o model iria encher esse objeto de dados e enviaria para view e a view se decidiria como apresentar essas informações.

Na verdade a grande “pegadinha” do padrão Observer é essa. que informações passar para o observador sem acoplar demais o sistema.

[quote=ualex]
Eu acho que deveria existir um objeto chamado DADOS.

o model iria encher esse objeto de dados e enviaria para view e a view se decidiria como apresentar essas informações.

Na verdade a grande “pegadinha” do padrão Observer é essa. que informações passar para o observador sem acoplar demais o sistema.[/quote]

Acho que entendi o que vc está fazendo!

A view não recebe dados pela update. Por padrão, uma View sempre mostra o modelo, o update serve para ela saber que o modelo foi mudado, então ela rpecisa atualizar o que msotra pro usuário.


public class Usuario{
 private int idade;
 //get set
}

public class view{
 public exibirUsuario(){
  System.out.println(usuario.getIdade());
 }
 
 public void atualizar(){
 //Mudaram os dados do usuario, mostre a mudança
  exeibirUsuario();
 }

}

Toda vez que os atributos do usuário mudarem seus atributos, atualizar() deve ser chamado.

[]s

assim eu acho pior porque no minimo a view vai ter que conhecer varios metodos do modelo e isso não é interessante. a view tem que saber o minimo possivel do modelo . por isso eu utilizei o padrão Command na hora de chamar a operação no modelo. mas deste jeito estrago na saida.

Mas se o modelo envia-se um objeto chamado Dados com um ArrayList de dados. a view só precisaria percorrer esse ArrayList e formatar ele na tela. ele não iria conhecer detalhes de implementação do modelo.

Então você não está usando Observer. Um observador tem que saber tirar informação do assunto.

O que você está fazendo é usar DTO, que tem outro uso.

DTO é igual VO ?

se for é ± isso.

Mas não preciso só disso, preciso do 2 porque não sei se DTO avisa sobre mudanças(esquema do obsevable).

na verdade é começar usar xwork. mas especificamente o pendulum.

Mais ou menos. A Sun chama DTO de Transfer Object, mas costumava chamar de VO. Só qeu DTO é um pattern do Fowler, não da Sun.

Para implementar um Observer, o observer tem que saber sobre o assunto. No seu caso, o Assunto é o DTO, não o Objetos de Negócio em si. exemplo:

public interface Assunto{
 public MeuDto getDto(); //Isso me lembra memento...
}

public class MeuObservador{
 public void atualizar(Assunto assunto){
  dto = assunto.getDto();
}
}

tudo bem esse getDTO vai devolver um DTO, que nesse caso seria o segundo argumento argumento do metodo update(Observable o,Object o).

parece que rodamos e chegamos no mesmo lugar :wink:

Não, a questão é que não existe motivo para você passar dois parâmetros.

[]s

tipo o observer não só para notificar sobre mudanças. ele também tem que avisar sobre o que mudou porque afinal é isso que interessa :slight_smile:

tipo como se alguem falasse para vc mudei, e dai ? ele tem que falar o que mudou. e passar mais informação entendeu é muito vago falar só mudei. E o primeiro argumento
java.util.Observer.update(Observable o,Object o) fala isso que ele mudou e mais algumas coisas mas ele não devolve o objeto que mudou(não existe um getSource). acho que por isso existe o segundo parametro para vc enviar a informação que for mais conviniente.

Assim você causa acoplamento.

O padrão observer supõe que existem diversos observadores,c ada um interessado em um ou outro aspecto.

Do jeito que você falou, quem avisa os observaores da mudança de estado 9que geralmente é o próprio assunto) tem que saber o que cada observador está interessado na classe, e passar para ele.

E já que é para passar, seria muito mais interessante passar apenas o que foi mudado. Se você não rpecisa da referência ao objeto para que o observador saiba o que mudou, para que passá-la?

Seu padrão pode ser substituído por uma simples chamada de método.

Não sei onde a Sun usa isso, se você achar um código que use para comentarmos, cola ai :slight_smile:

[]s