Kristián Filo
Kristián Filo

Reputation: 865

Separating GUI from logic in Java

I went through plenty of articles and (imo too complex) examples of how to separate GUI from the rest of the program logic in Java, and to be completely honest, I still have no clue.

Could someone give me a hint of how to do it on this simple example, with just one button?

Let's say there is a button in the Window class:

JButton firstButton = new JButton("My first button");      
btnCreateProject.setBounds(100, 100, 80, 30);
frame.getContentPane().add(firstButton);

and let's say this button would call a constructor of the Employee class. So, I could do it like this:

JButton firstButton = new JButton("My first button");
firstButton.addActionListener(new ActionListener() {
   public void actionPerformed(ActionEvent e) {
      .....constructor calling, and rest of the code....
   }
});
btnCreateProject.setBounds(100, 100, 80, 30);
frame.getContentPane().add(firstButton);

But this is exactly what I DO NOT want. I want my Window class to be just pure GUI, with buttons, radio boxes, and other stuff, with it's properties. Nothing more. I would like to have all the listeners in another class, let's say Controller, and from this class I would call all the needed methods.

Could someone give me an example of how to do it with this simple button? Thank you very much.

Upvotes: 4

Views: 10885

Answers (6)

notanormie
notanormie

Reputation: 455

What you should do is write a method where appropriate to access datamodel (supposing you have one) and do the work there, and just call the method from button click.

firstButton.addActionListener(e -> logicClass.addEmployeeToFirm());

or you could event write a custom Listener that would call/do that logic (a class that implements ActionListener).

Upvotes: 1

yngwietiger
yngwietiger

Reputation: 1184

You could always just implement something like:

Controller.handleActionEvent(ActionEvent e);

and delegate your button click to that. And if you don't want your controller to know about Swing, you could always create some interface and wrap the Swing ActionEvent in some kind of event of your creation.

Or just implement an Observer (Listener) pattern in the UI class (or you could have a "registry" class that sits in the middle). When the button is clicked, the UI would delegate the event to all registered listeners (implementing some interface that you define). So the Controller (client) would just tell the UI "notify me when Button X is clicked" and the UI would just delegate to all interested parties.

So, the UI wouldn't need to know who to call explicitly. But the controller would have to know who he wants to listen to.

Upvotes: 1

Soley
Soley

Reputation: 1776

I had the same issue with my projects, but finally I decided to use a concept I learned in android programming. Here is how it works:

I add an identified to my objects (for buttons, menus, etc I use setActionCommand method) so I can use the same identifier for the same operations for different components.

All my objects call one controller with their getActionCommand as cmd and the object itself. Within the controller, I control the cmd and call the proper method from it.

It is much more easier to control the GUI and the rest of the program.

It worked for me this way like charm.

import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JDialog;

/**
 *
 * @author Pasban
 */
public class ActionListenerTest {

    public static void main(String[] args) {
        JButton b1 = new JButton("My first button");
        JButton b2 = new JButton("My first button");

        b1.setActionCommand("BTN_1");
        b2.setActionCommand("BTN_2");

        //put this in another class
        ActionListener controller = new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                String cmd = e.getActionCommand().toUpperCase().trim();
                System.out.println(cmd);

                //System.out.println(e.getSource()); // cast to anything you have in your mind about the caller

                if (cmd.equals("BTN_1")) {
                    System.out.println("BUTTON 1 is clicked");
                } else if (cmd.equals("BTN_2")) {
                    System.out.println("BUTTON 2 is clicked");
                }
            }
        };

        b1.addActionListener(controller);
        b2.addActionListener(controller);

        JDialog frame = new JDialog();
        frame.setSize(new Dimension(300, 300));
        frame.setLocationRelativeTo(null);
        frame.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);


        frame.getContentPane().setLayout(new FlowLayout());
        frame.getContentPane().add(b1);
        frame.getContentPane().add(b2);

        frame.setVisible(true);

    }
}

Upvotes: 1

ControlAltDel
ControlAltDel

Reputation: 35011

Inside your controller, create a method

Action get[Operation]Handler(); //Fill in [Operation] will what your button functionality should do

which returns an inner class that implements the proper functionality.

Then, in your GUI code, do

JButton b = ...;
b.setAction(controller.get[Operation]Handler());

Upvotes: 0

John Bollinger
John Bollinger

Reputation: 180161

You are drawing a distinction of degree, not kind. Your GUI code cannot be completely separated from program logic, else it would be a static work of modern art.

The various kinds of AWT/Swing listener classes do separate GUI from logic, into altogether separate classes. You do not, however, need to implement listeners as anonymous inner classes; perhaps it would feel better to you to implement them as ordinary, top-level classes instead. Alternatively, you might find that it suits you to model program behaviors via classes implementing the Action interface. Then you can assign those behaviors to controls (buttons, menu items) via the controls' setAction() method.

Any way around, however, the code that sets up your GUI has to somehow know about both the GUI components and the logic that needs to be hooked up to them, else there's no way it could do its job.

Upvotes: 3

JFPicard
JFPicard

Reputation: 5168

This is a constructor for a new class:

new ActionListener() {
   public void actionPerformed(ActionEvent e) {
      .....constructor calling, and rest of the code....
   }
}

You could make a class like:

public class ActionListenerTest implements ActionListener { ... }

Then make something like this:

firstButton.addActionListener(new ActionListenerTest());

Upvotes: 1

Related Questions