Como implementar ActionListener em uma estrutura MVC - Swing

Bom dia pessoal, estou com uma dúvida de qual seria a melhor forma de implementar está minha arquitetura.

Vamos la!!!

Estou fazendo uma pequena aplicação, onde eu tenho a minha tela de login, dentro desta tela de login existem 3 componentes entre eles está o meu botão de entrar, nele eu adiciono um actionListener, a minha dúvida é a seguinte, o que eu devo passar para este actionListener ? Eu estou pensando em passar o JFrame inteiro e dentro dele eu separo os dados da tela e mando eles para a controler da tela de login. Não sei se ficou clara a ideia, mais o que vcs acham dela ? Qual seria a melhor forma de implementar ?

Vou colocar o code aqui:

Tela de login:


package videolocadora.helio.gui;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JRootPane;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.LineBorder;

import videolocadora.helio.JavaSource.infra.msg.MensagensExceptionEnum;
import videolocadora.helio.gui.action.LoginEntradaActionListener;
import videolocadora.helio.gui.componentes.UpperCaseTextField;

import com.birosoft.liquid.LiquidLookAndFeel;

public class LoginGui extends JFrame{

	/**
	 * Atributo referenta a criação do painel principal
	 */
	private JPanel painelCentralView;
	
	
	public LoginGui() {
		alteraLayout();
		
		painelCentralView = new JPanel();
		painelCentralView.setLayout(null);
		
		JLabel teste = new JLabel(new ImageIcon("src/videolocadora/helio/gui/image/imageLogo.png"));
		teste.setBounds(0, 0, 90, 140);
		
		JLabel teste2 = new JLabel(new ImageIcon("src/videolocadora/helio/gui/image/textLogo.png"));
		teste2.setBounds(230, 10, 150, 30);
		
		
		JLabel login = new JLabel("Usuário:");
		login.setBounds(100, 50, 50, 20);
		
		JLabel senha = new JLabel("Senha:");
		senha.setBounds(100, 80, 50, 20);
		
		
		
		
		JTextField loginField = new UpperCaseTextField(10);
		loginField.setBorder(new LineBorder(new Color(54,166,229)));
		loginField.setBackground(new Color(20,65,150));
		loginField.setForeground(new Color(0xffffff));
		loginField.setFont(new Font ("Serif", Font.PLAIN, 12));
		loginField.setBounds(160, 50, 150, 20);
		
		JPasswordField senhaField = new JPasswordField();
		senhaField.setBorder(new LineBorder(new Color(54,166,229)));
		senhaField.setBackground(new Color(20,65,150));
		senhaField.setForeground(new Color(0xffffff));
		senhaField.setBounds(160, 80, 150, 20);

		
		JButton botaoEntrar = new JButton("ENTRAR");
		botaoEntrar.addActionListener(new LoginEntradaActionListener(this));
		botaoEntrar.setBounds(180, 115, 90, 20);
		
		
		

		
		
		painelCentralView.add(login);
		painelCentralView.add(senha);
		painelCentralView.add(loginField);
		painelCentralView.add(senhaField);
		painelCentralView.add(teste2);
		painelCentralView.add(teste);
		painelCentralView.add(botaoEntrar);
		painelCentralView.setBackground(Color.WHITE);
		setSize(new Dimension(400,170));
		setLocationRelativeTo(null);
		setTitle("Helio");
		add(painelCentralView);
		setVisible(true);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
	
	
	/**
	 * Método responsável por setar o layout no estilo MAC.
	 */
	private void alteraLayout(){
		try {
			setUndecorated(true);
			getRootPane().setWindowDecorationStyle(JRootPane.FRAME);
			UIManager.setLookAndFeel(new com.birosoft.liquid.LiquidLookAndFeel());
			LiquidLookAndFeel.setLiquidDecorations(true,"mac");
		    LiquidLookAndFeel.setShowTableGrids(true);
		    LiquidLookAndFeel.setStipples(false);
			SwingUtilities.updateComponentTreeUI(this);
		} catch (Exception e) {
			JOptionPane.showMessageDialog(null, MensagensExceptionEnum.ERRO_CARREGAR_LAYOUT_APLICAÇÃO, "ERRO", JOptionPane.ERROR);
		}
	}
	
	
	public static void main(String[] args) {
		LoginGui teste = new LoginGui();
	}
}

Classe de Ação


package videolocadora.helio.gui.action;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class LoginEntradaActionListener implements ActionListener{

	private JFrame main;
	
	
	public LoginEntradaActionListener(JFrame main) {
		this.main = main;
	}
	
	public void actionPerformed(ActionEvent e) {
		main.dispose();
	
	}
	

}

PS: Não liguem para o codigo zuado, estou só fazendo uns testes.

Niguem para ajudar na discusão ? :frowning:

Olha, eu não vejo porque colocar os Listeners em um pacote Action não.
Os Listeners são associados aos componentes visuais e, através deles, se faz a comunicação com o Controller.
Para mim, é muito melhor deixar os Listeners na própria VIEW e eles fazerem a chamada de Controllers específicos:

package teste.view;

public class WinTeste extends JFrame
{
   public WinTeste()
   {
      JButton anButton = new JButton("Testando...");
      anButton.addActionListener( new ListenerTestando() );
   }

   private class ListenerTestando implements ActionListener
   {
       public void actionPerformed(ActionEvent e)
       {
          Controller.chamaAlgumaCoisa(); 
       }
   }
}

Abraços!

Eu repeito o pensamento do Nicolas, entretanto eu discordo dele, acho que o mais interessante é usar a classe Action como o proprio controler e manter a view só como view, eu gosto de trabalhar com poucas classes controladoras, geralmente seto actionComand nos botoes e pego o parametro pelo arg, daí posso direcionar varias ações diferentes para um só controlador, desde que não viole a organização dos modulos do programa.

No caso do Nicolas, da a impressao que vc tem um microController chamando um MacroController.

hum vc poderia dar um exemplo ganondorfan ? Só para eu ter uma ideia de como vc faz ?

Nicolas imagine que vc tenha uma tela muito complicado com muitas ações e com muitos paineis diferentes, usando a sua estrategia vc n acha que a tela ficaria muito ruim de dar manutenção e de se adicionar funcionalidades ?

A ideia de ter um pacote action não é que ele esteja fora da view, ele faz parte da view, a minha ideia é que ele instancie um classe de controle dentro dele, e está classe tome parte das regras de negocio. A action ligaria a resposta da controle nas ações da tela.

Não sei se ficou claro o que eu escrevi XD

Na verdade, a diferença minha e sua é a seguinte: Eu uso as classes “Action” dentro das próprias Views, sem criá-las em um pacote separado.

Eu faço…

public class MinhaView extends JFrame
{
   public MinhaView()
   {
      JButton anButtonPrimeiro = new JButton("Primeiro");
      anButtonPrimeiro.addActionListener( new ListenerPrimeiro() );
      JButton anButtonPrimeiro = new JButton("Segundo");
      anButtonPrimeiro.addActionListener( new ListenerSegundo() );
      JButton anButtonPrimeiro = new JButton("Terceiro");
      anButtonPrimeiro.addActionListener( new ListenerTerceiro() );
   }
   private class ListenerPrimeiro implements ActionListener
   {
 
   }
   private class ListenerSegundo implements ActionListener
   {
 
   }
   private class ListenerTerceiro implements ActionListener
   {
 
   } 
}

Enquanto você faz…

package meuprojeto.view;

public class MinhaView extends JFrame
{
   public MinhaView()
   {
      JButton anButtonPrimeiro = new JButton("Primeiro");
      anButtonPrimeiro.addActionListener( new LocalizadorListeners.getListenerPrimeiro() );
      JButton anButtonPrimeiro = new JButton("Segundo");
      anButtonPrimeiro.addActionListener( new LocalizadorListeners.getListenerSegundo() );
      JButton anButtonPrimeiro = new JButton("Terceiro");
      anButtonPrimeiro.addActionListener( new LocalizadorListeners.getListenerTerceiro() );
   }
}

package meuprojeto.action.listeners;

public class LocalizadorListeners
{
   public static ListenerPrimeiro getListenerPrimeiro()
   {
      return new ListenerPrimeiro();
   }
   public static ListenerPrimeiro getListenerSegundo()
   {
      return new ListenerSegundo();
   }
   public static ListenerPrimeiro getListenerTerceiro()
   {
      return new ListenerTerceiro();
   }
}
public class ListenerPrimeiro implements ActionListener
{
 
}
public class ListenerSegundo implements ActionListener
{
 
}
public class ListenerTerceiro implements ActionListener
{
 
} 

A responsabilidade de “ligar a resposta da controle nas ações da tela” é parte das classes Controllers.
No fim, dá no mesmo, não acha?

Minha visão dos ActionListeners é de que eles servem apenas para fazer o tratamento de ações da View, talvez em alguns casos você não queira ainda chamar o controller para executar alguma ação …

Exemplo:

Ao clicar em “Salvar” em uma view, você pode querer verificar se algum campo deixou de ser preenchido … ou Ao clicar “Cancelar” você pode exibir um aviso de que as informações irão ser perdidas … Penso que isso são ações que são de responsabilidade da própria view …

Agora, depois de clicar em “Salvar”, visto que está tudo ok, podemos chamar a ação de “insert” ou “update” do controller do módulo … e aí sim, outra classe …

Pensando assim, acho melhor ficarem dentro da classe da view …

Concordo!

Neste caso, eu já discordo um pouco. Eu passaria toda a responsabilidade de manipulação de dados (validação, correção, operações no Banco de Dados) para o Controller. A View, como o próprio nome já diz, é somente responsável por apresentar dados para o usuário, e nada mais. Porque dar a ela a responsabilidade de validação sendo que o próprio Controller pode fazer isso?
Um Listener para isso eu faria assim:


/*-----------------------------------------------------------------------------
                              A Classe de Controle
-----------------------------------------------------------------------------*/
public class Controller
{
   public String validarDados(Map<String, Object> dados)
   {
      if (dados.get("nome").equals("") || dados.get("nome") == null)
      {
         return "O campo 'Nome' é obrigatório!";
      }

      return "";
   }

   public void salvarDados(Map<String, Object> dados)
   {
      // Faz todo o processamento...
   }
}

/*-----------------------------------------------------------------------------
                              A Classe de View
-----------------------------------------------------------------------------*/
public class MinhaView extends JFrame
{
   private transient JButton anButton;
   private transient JTextField anTextField;

   public MinhaView()
   {
      anTextField = new JTextField("");
      
      anButton = new JButton("Salvar");
      anButton.addActionListener( new ListenerSalvar() );
   }

   private Map<String, Object> juntarDadosFormulario()
   {
      Map<String, Object> dados = new HashMap<String, Object>();
      
      dados.put("nome", anTextField.getText());

      return dados;
   }
   private class ListenerSalvar implements ActionListener
   {
      public void actionPerformed(ActionEvent e)
      {
         Controller anController = new Controller();
         
         String anMensagemErro = anController.validarDados(juntarDadosFormulario());
         if (anMensagemErro.equals(""))
         {
            anController.salvarDados(dados);
         }
         else
         {
            JOptionPane.showMessageDialog(null, anMensagemErro, "Erro!");
         }
      }
      else
   }
}

Eu me sinto mais à vontade assim =)