0

So what I am trying to accomplish is to add ActionListener to a button which is defined in another class, without breaking encapsulation of this button.

My GUI class:

public class GUI extends JFrame {  

    private JButton button; 

    public GUI () { 
        this.button = new JButton ();
    }

    public void setText (Text text) {
        this.button.setText (text);
    }

    public JButton getButton () {
        return this.button;
    }    
}

My Game class:

public class Game { 

    private GUI gui;  

    public Game () {
        this.gui = new GUI ();
        this.gui.getButton ().addActionListener (new ActionListener () {
            public void actionPerformed (ActionEvent evt) {
                play ();                       
            }
        });  
    }

    public void play () {
        this.gui.setText ("Play");
    }
}

Then I call a new Game instance in the Main class.

I would like to get rid of the getter in GUI class, otherwise there is no point in using text setter or setters similar to that.

When I add ActionListener to GUI constructor, I have no access to Game methods than. Is there a solution that I don't see?

Ivan Horňák
  • 25
  • 1
  • 7
  • 1
    What are you trying to encapsulate and why? Simply add a `addActionListenerToButton` method to the `GUI` might solve the "issue" you have right now. – luk2302 Dec 02 '17 at 18:24
  • When I add ActionListener to GUI i can't use play () method from Game class, because I have already created instance of Game in Main class. – Ivan Horňák Dec 02 '17 at 18:30

2 Answers2

2

Normally when I do this, I add an interface that describes the View (GUI), and then have the view implement that interface.

public interface MyView {
    void addActionListener( ActionListener l );
}

And the view:

public class GameGui implements MyView {
   // lots o' stuff

    public void addActionListener( ActionListener l ) {
       button.addActionListener( l );
    }
}

Then your main code is free from dependencies on what kind of view you actually implement.

public class Main {
    public static void main( String... args ) {
       SwingUtils.invokeLater( Main::startGui );
    }

    public static void startGui() {
       MyView gui = new GameGui();
       gui.addActionListener( ... );
    }
}

Don't forget that Swing is not thread safe and must be invoked on the EDT.

markspace
  • 10,621
  • 3
  • 25
  • 39
0

Let the GUI add the action listener to the button, let the Game create the action listener:

public class GUI extends JFrame { 

    public void addActionListenerToButton(ActionListener listener) {
        button.addActionListener(listener);
    }

    ....

}

public class Game { 

    private GUI gui;  

    public Game () {
        this.gui = new GUI ();
        this.gui.addActionListenerToButton (new ActionListener () {
            public void actionPerformed (ActionEvent evt) {
                play ();                       
            }
        });  
    }

    ...
}

Alternatively just pass in a functional interface instead of a fully built ActionListener.

luk2302
  • 55,258
  • 23
  • 97
  • 137