Search code examples
javaswingdesign-patternsobserver-patternchain-of-responsibility

Java ActionListeners to stand-alone classes


Is there any way to move actionListener classes to stand-alone classes?

I made an exaple using Java and MVC design pattern. I have 3 buttons that change background color.

Here's Model

public class ChangeFontColorApplicationModel {
    public ChangeFontColorApplicationModel(){}
}

View

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

public class ChangeFontColorApplicationView extends JFrame{
private JButton changeToYellowButton = new JButton("Yellow font");
private JButton changeToBlackButton = new JButton("Black font");
private JButton changeToBlueButton = new JButton("Blue font");

public ChangeFontColorApplicationView(){
    super("Change font");
    JPanel buttonPanel = new JPanel();

    buttonPanel.add(changeToYellowButton);
    buttonPanel.add(changeToBlackButton);
    buttonPanel.add(changeToBlueButton);

    this.add(buttonPanel);
}

public void addButtonActionListener(){
    changeToYellowButton.addActionListener(new yellowBackgroundHandler());
    changeToBlackButton.addActionListener(new yellowBackgroundHandler());
    changeToBlueButton.addActionListener(new yellowBackgroundHandler());
}
}

Controller

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

public class ChangeFontColorApplicationController {
private ChangeFontColorApplicationView changeFontColorApplicationViewObject;
backgroundHandler yellowBackgroundHandlerObject = new yellowBackgroundHandler();
backgroundHandler blackBackgroundHandlerObject = new blackBackgroundHandler();
backgroundHandler blueBackgroundHandlerObject = new blueBackgroundHandler();

public ChangeFontColorApplicationController(ChangeFontColorApplicationView changeFontColorApplicationViewObject){
    this.changeFontColorApplicationViewObject = changeFontColorApplicationViewObject;

    yellowBackgroundHandlerObject.setNextHandlerInChain(blackBackgroundHandlerObject);
    blackBackgroundHandlerObject.setNextHandlerInChain(blueBackgroundHandlerObject);

    this.changeFontColorApplicationViewObject.addButtonActionListener();
}
}

Now I created an interface that handles the button events

public interface backgroundHandler extends ActionListener{
public void setNextHandlerInChain(backgroundHandler nextInChain);
public void handleCommand(String getActionCommandString);
public void actionPerformed(ActionEvent event);
}

Every class that implements the interfaces, handles the ActionEvent. If it cannot, it passes to the next class (as Chain of responsibility)

yellow font handler

import java.awt.Color;
import java.awt.event.ActionEvent;

public class yellowBackgroundHandler implements backgroundHandler{
private backgroundHandler nextInChain;
private ChangeFontColorApplicationView ChangeFontColorApplicationViewObject = new ChangeFontColorApplicationView();

public yellowBackgroundHandler(){}

@Override
public void setNextHandlerInChain(backgroundHandler nextInChain) {
    this.nextInChain = nextInChain;
}

@Override
public void handleCommand(String getActionCommandString) {
    if(getActionCommandString.equalsIgnoreCase("Yellow font")){
        ChangeFontColorApplicationViewObject.setBackground(Color.yellow);
    }
    else {
        nextInChain.handleCommand(getActionCommandString);
    }
}

@Override
public void actionPerformed(ActionEvent event) {
    try{
        handleCommand(event.getActionCommand());
    }
    catch (Exception exeption){
        ChangeFontColorApplicationViewObject.setTitle(exeption.toString());            
    }
}
}

black font handler

import java.awt.Color;
import java.awt.event.ActionEvent;

public class blackBackgroundHandler implements backgroundHandler{
private backgroundHandler nextInChain;
private ChangeFontColorApplicationView ChangeFontColorApplicationViewObject = new ChangeFontColorApplicationView();

public blackBackgroundHandler(){}

@Override
public void setNextHandlerInChain(backgroundHandler nextInChain) {
    this.nextInChain = nextInChain;
}

@Override
public void handleCommand(String getActionCommandString) {
    if(getActionCommandString.equalsIgnoreCase("Black font")){
        ChangeFontColorApplicationViewObject.setBackground(Color.BLACK);
    }
    else {
        nextInChain.handleCommand(getActionCommandString);
    }
}

@Override
public void actionPerformed(ActionEvent event) {
    try{
        handleCommand(event.getActionCommand());
    }
    catch (Exception exeption){
        ChangeFontColorApplicationViewObject.setTitle(exeption.toString());            
    }
}
}

blue font handler

import java.awt.Color;
import java.awt.event.ActionEvent;

public class blueBackgroundHandler implements backgroundHandler{
private backgroundHandler nextInChain;
private ChangeFontColorApplicationView ChangeFontColorApplicationViewObject = new ChangeFontColorApplicationView();

public blueBackgroundHandler(){}

@Override
public void setNextHandlerInChain(backgroundHandler nextInChain) {
    this.nextInChain = nextInChain;
}

@Override
public void handleCommand(String getActionCommandString) {
    if(getActionCommandString.equalsIgnoreCase("Μπλε φόντο")){
        ChangeFontColorApplicationViewObject.setBackground(Color.BLUE);
    }
    else {
        ChangeFontColorApplicationViewObject.setTitle("This is the back end of COR");
    }
}

@Override
public void actionPerformed(ActionEvent event) {
    try{
        handleCommand(event.getActionCommand());
    }
    catch (Exception exeption){
        ChangeFontColorApplicationViewObject.setTitle(exeption.toString());            
    }
}
}

Finally I create the class with the main method

public class ChangeFontColorApp {
public static void main(String[] args){
    ChangeFontColorApplicationView changeFontColorApplicationViewObject = new ChangeFontColorApplicationView();
    ChangeFontColorApplicationController changeFontColorApplicationControllerObject = new ChangeFontColorApplicationController(changeFontColorApplicationViewObject);

    changeFontColorApplicationViewObject.setVisible(true);
    changeFontColorApplicationViewObject.setSize(600, 600);
       changeFontColorApplicationViewObject.setDefaultCloseOperation(ChangeFontColorApplicationView.EXIT_ON_CLOSE);
}
}

Is there any way to make this work? I don't want to have an inner class for event handling with a multiple if blick in it. Any suggestions would be really appreciated.


Solution

  • That won't work. But, the problem is not because you have standalone classes (using standalone classes is fine). Rather, the problem is:

    In view class, you have:

    public void addButtonActionListener(){
        changeToYellowButton.addActionListener(new yellowBackgroundHandler());
        changeToBlackButton.addActionListener(new yellowBackgroundHandler());
        changeToBlueButton.addActionListener(new yellowBackgroundHandler());
    }
    

    this means that clicks of all buttons are handled by yellowBackgroundHandler. But, yellowBackgroundHandler only accepts clicks from changeToYellowButton. In addition to that, yellowBackgroundHandler is not configured with next handler.

    To fix this, in view class, change this:

    public void addButtonActionListener(){
        changeToYellowButton.addActionListener(new yellowBackgroundHandler());
        changeToBlackButton.addActionListener(new yellowBackgroundHandler());
        changeToBlueButton.addActionListener(new yellowBackgroundHandler());
    }
    

    to

    public void addButtonActionListener(backgroundHandler handler){
        changeToYellowButton.addActionListener(handler);
        changeToBlackButton.addActionListener(handler);
        changeToBlueButton.addActionListener(handler);
    }
    

    and in controller class, change this:

    this.changeFontColorApplicationViewObject.addButtonActionListener();
    

    to

    this.changeFontColorApplicationViewObject.addButtonActionListener(yellowBackgroundHandlerObject);
    

    Another problem that I see is that yellowBackgroundHandler, blackBackgroundHandler, and blueBackgroundHandler have private variable ChangeFontColorApplicationViewObject, which points to its own instance of ChangeFontColorApplicationView. We need this variable to point to the instance created in the main method. To do this, change this:

    private ChangeFontColorApplicationView ChangeFontColorApplicationViewObject = new ChangeFontColorApplicationView();
    public blueBackgroundHandler(){}
    

    to:

    private ChangeFontColorApplicationView ChangeFontColorApplicationViewObject;
    public blueBackgroundHandler(ChangeFontColorApplicationView ChangeFontColorApplicationViewObject){
      this.ChangeFontColorApplicationViewObject = ChangeFontColorApplicationViewObject;
    }
    

    and change this:

    private ChangeFontColorApplicationView ChangeFontColorApplicationViewObject = new ChangeFontColorApplicationView();
    public blackBackgroundHandler(){}
    

    to:

    private ChangeFontColorApplicationView ChangeFontColorApplicationViewObject;
    public blackBackgroundHandler(ChangeFontColorApplicationView ChangeFontColorApplicationViewObject){
      this.ChangeFontColorApplicationViewObject = ChangeFontColorApplicationViewObject;
    }
    

    and change this:

    private ChangeFontColorApplicationView ChangeFontColorApplicationViewObject = new ChangeFontColorApplicationView();
    public blueBackgroundHandler(){}
    

    to:

    private ChangeFontColorApplicationView ChangeFontColorApplicationViewObject;
    public blueBackgroundHandler(ChangeFontColorApplicationView ChangeFontColorApplicationViewObject){
      this.ChangeFontColorApplicationViewObject = ChangeFontColorApplicationViewObject;
    }
    

    finally, change this:

    backgroundHandler yellowBackgroundHandlerObject = new yellowBackgroundHandler();
    backgroundHandler blackBackgroundHandlerObject = new blackBackgroundHandler();
    backgroundHandler blueBackgroundHandlerObject = new blueBackgroundHandler();
    
    public ChangeFontColorApplicationController(ChangeFontColorApplicationView changeFontColorApplicationViewObject){
        this.changeFontColorApplicationViewObject = changeFontColorApplicationViewObject;
    
        yellowBackgroundHandlerObject.setNextHandlerInChain(blackBackgroundHandlerObject);
        blackBackgroundHandlerObject.setNextHandlerInChain(blueBackgroundHandlerObject);
    
        this.changeFontColorApplicationViewObject.addButtonActionListener();
    }
    

    to

    backgroundHandler yellowBackgroundHandlerObject = new yellowBackgroundHandler();
    backgroundHandler blackBackgroundHandlerObject = new blackBackgroundHandler();
    backgroundHandler blueBackgroundHandlerObject = new blueBackgroundHandler();
    
    public ChangeFontColorApplicationController(ChangeFontColorApplicationView changeFontColorApplicationViewObject){
        this.changeFontColorApplicationViewObject = changeFontColorApplicationViewObject;
        this.yellowBackgroundHandlerObject = new yellowBackgroundHandler(this.changeFontColorApplicationViewObject);
        this.blackBackgroundHandlerObject = new blackBackgroundHandler(this.changeFontColorApplicationViewObject);
        this.blueBackgroundHandlerObject = new blueBackgroundHandler(this.changeFontColorApplicationViewObject);
    
        yellowBackgroundHandlerObject.setNextHandlerInChain(blackBackgroundHandlerObject);
        blackBackgroundHandlerObject.setNextHandlerInChain(blueBackgroundHandlerObject);
    
        this.changeFontColorApplicationViewObject.addButtonActionListener(this.changeFontColorApplicationViewObject);
    }