2
\$\begingroup\$

I'm a novice at Java and just beginning with Swing. I’m trying to figure out how to separate event logic from the presentation logic. Look at the following classes:

GUITest.java

package guitest;
import java.util.ArrayList;
public class GuiTest {     
    public static void main(String args[]) {
      new MainFrame2();}
}

MainFrame2.java

    package guitest;
    import javax.swing.*;
    import java.awt.event.*;
public class MainFrame2 extends JFrame implements ActionListener{
    JPanel pane = new JPanel();
    JButton butt = new JButton("Push Me"); 
    JTextArea ta = new JTextArea("Push Me");
    int i = 0;
    MainFrame2(){
        super("My GUITest");
        setVisible(true);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);     
        setBounds(1000,100,500,500); //From upper left corner (right, down, width, height)
        this.add(pane); 
        pane.setLayout(null);
        butt.setSize(300,20);
        butt.setLocation(10,10); 
        butt.addActionListener(this);
        pane.add(butt);   
        ta.setBounds(0,50,300,30);
        pane.add(ta);
    } //end constructor

public void actionPerformed(ActionEvent event){
        i++;
        switch(i){
            case 1: ta.setText("And Then Just Touch Me");       
                break;
            case 2: ta.setText("Till I Can Get My");
                break;
            case 3: ta.setText("Satisfaction");
                break;
            case 4: ta.setText("Push Me");
                break;
            }
    }   
} //end class

This simple example works. When the button is pushed, the text area changes. But what if the text is produced by 100 lines of code? Say, if the results of a complex SQL query were pasted there? I wouldn't want to put that 100 lines of code in the MainFrame2 class. I’d want to put it into another class, so I've separated my presentation code from my data manipulation code.

Going further, what if I had several methods that weren't tied to a particular object? Say, if clicking a button was supposed to write to a log file or send an e-mail? I’d see a need to put that code somewhere outside of the MainFrame2 class, but I wouldn't see a need to make a LogFile class or an Email class. I just need a group of methods that I can call without having to make a huge number of new classes.

I found a workaround. I made a class called Misc, for all the miscellaneous methods I need to call. Then I instantiate a new Misc object in the MainFrame2 class. Then I call the miscellaneous methods from that object. Here are the Misc class and the adjusted MainFrame2 class.

Misc.java

package guitest;
public class Misc {    
    Misc(){} 
public static String lyrics(int i){
            if(i == 1){return("And Then Just Touch Me");}                        
            if(i == 2){return("Till I Can Get My");}
            if(i == 3){return("Satisfaction");}
            if(i == 4){return("Push Me");}
            return("Something's wrong. I should always be 1-4.");
    }
public static void logging(String s){
    //Write s to some file        
    }   
public static void notification(String sender, String receipient, String subject, String body){
    //Send an e-mail to someone.
    }
}

MainFrame2.java

    package guitest;
    import javax.swing.*;
    import java.awt.event.*;
public class MainFrame2 extends JFrame implements ActionListener{
    JPanel pane = new JPanel();
    JButton butt = new JButton("Push Me"); 
    JTextArea ta = new JTextArea("Push Me");
    int i = 0;
    MainFrame2(){
        super("My GUITest");
        setVisible(true);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);     
        setBounds(1000,100,500,500); //From upper left corner (right, down, width, height)
        this.add(pane); 
        pane.setLayout(null);
        butt.setSize(300,20);
        butt.setLocation(10,10); 
        butt.addActionListener(this);
        pane.add(butt);   
        ta.setBounds(0,50,300,30);
        pane.add(ta);
    } //end constructor

public void actionPerformed(ActionEvent event){
        i++;
        ta.setText(ot.lyrics(i));
        if(i == 4){i = 0;}
    }   
} //end class

This meets my needs: The code for all those complex methods is

  1. separated from the presentation class
  2. accessible by the presentation’s event handlers

But it seems horribly inelegant. It tosses all the random, one-off methods into a class of leftovers. Is there a better way to do this?

\$\endgroup\$
4
\$\begingroup\$

You're on the right way. In principle this is the way used in Java. There are only some differences in real code.

Your class Misc is normally named a Service. A Service class does the business logic of your application. Also normally you don't use static methods but instance methods to do you business logic.

The Service class is normally created outside the GUI code (e.g. in your main() method). You then set the Service class into your GUI class either via the constructor or via a setter.

In more advanced setups you use frameworks like spring to wire up your application.

Also you would have three different services in your case, one for the lyrics (with several methods e.g. to get a single line or the complete lyrics at once), one for the notification and a third for logging (if you don't use a framework for this).

Also the business logic is often parted into two layers, one for the pure business logic and another for the data access (e.g. database access).

Also some other notes:

  1. It is usual to create the GUI first and call setVisible() in the last.
  2. The GUI initialization code is normally in its own private method that is called from the constructor.
\$\endgroup\$
2
\$\begingroup\$

I'm really sorry if I may sound harsh, but there is much things that are wrong in your example.

There is no notion of access modifier being use. Almost everything is by default, which is the package modifier. So all your code is only working because everything is in the same package. If you change the package of any of the three class (Misc, MainFrame2 and GuiTest) nothing will work anymore. This is one of the first step you need to take, decide what can be accessed and what not.

Misc

You should not return "Something's wrong. I should always be 1-4." bad data as mechanism to show that the method failed. You should fix your method to avoid encountering the wrong case, or throw an exception if you're not in a valid state.

The method lyrics should know by itself which lyrics should be return to the asker, since you want the lyrics in order. One of the big flaws of this class is that it's static. A class that resemble a controller should not be static. You should make an instance and inject your instance where you need the controller. This would help in assuring that no one else is calling the static method from another part of the project and change your order that you should receive your lyrics.

That i was declared in MainFrame2 and use in Misc was very unusual and should be avoided at all cost. If you need a variable to be accessible by other classes make get/set and use those to control the state of the variable.

package guitest;

public class Misc {
    private int count = 0;

    public String lyrics() {
        if(count == 4) {
            count = 0;
        }
        count++;
        if (count == 1) {
            return "And Then Just Touch Me";
        } else if (count == 2) {
            return "Till I Can Get My";
        }else if (count == 3) {
            return ("Satisfaction");
        } else {
            return "Push Me";
        }
    }

    public void logging(String s) {
        // Write s to some file
    }

    public void notification(String sender, String recipient,
            String subject, String body) {
        // Send an e-mail to someone.
    }
}

MainFrame2

You need to make your class public, you don't need to hide it. You want to use it where you want when you want. Extending JFrame is a bad idea, you don't inheritance favor composition. Your actionPerfomed is a bit too "intelligent", it keep track of which lyrics you need to show. This method should not know what she need but should ask someone what should be the next lyrics. You should not use pane.setLayout(null);, try to use a real layout and please don't use absolute positioning. This is hell for maintenance ( I didn't implement this change though). So here is your class, clean up :

package guitest;

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

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;

public class MainFrame2 implements ActionListener {
    private JFrame principalWindow = new JFrame("My GUITest");
    private JPanel panel = new JPanel();
    private JButton pushButton = new JButton("Push Me");
    private JTextArea output = new JTextArea("Push Me");

    private Misc miscController;

    public MainFrame2(Misc controller) {
        miscController = controller;
        principalWindow.setVisible(true);
        principalWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        principalWindow.setBounds(1000, 100, 500, 500); // From upper left
                                                        // corner (right, down,
                                                        // width, height)
        panel.setLayout(null);
        pushButton.setSize(300, 20);
        pushButton.setLocation(10, 10);
        pushButton.addActionListener(this);
        panel.add(pushButton);
        output.setBounds(0, 50, 300, 30);
        panel.add(output);
        principalWindow.add(panel);
    }

    public void actionPerformed(ActionEvent event) {
        output.setText(miscController.lyrics());
    }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.