In Java, is it good practice to pass through listeners (or objects) where it affects the parent component?

StackOverflow https://stackoverflow.com/questions/23691062

  •  29-07-2023
  •  | 
  •  

Question

More of a theory question, possibly a silly one but was curious to know if there's a better way of implementing it.

Ok so the question:

What is the best way of implementing two child-classes to interact with each other when an event from one of them is fired? I didn't word that well, so perhaps a diagram would be of use.

                                    MainPanel
                                     /     \
                                    /       \
                                   /         \
                                Child #1    Child #2

So in the above 'diagram' (yey amazing drwaz skills), there's a MainPanel that holds/instantiates two child panels. These components are outside of each others scope, unless I'm mistaken, and therefore can not directly interact with each other.

Lets say Child #1 has a JButton, and when the event is fired, Child #2 changes it's JLabel from "Hi" to "Bye". How would you go about doing so in the 'best' way possible?


Currently, my implementation of the above would involve having Child #1's constructor call for a Listener to be passed through it, where it sets the listener on the JButton to whatever listener the object is instantiated with.

Code of that would look like :

public class MainPanel extends JPanel {
    private Child1 child1;
    private Child2 child2;
    private ActionListener listener;
    
    public MainPanel(){
          listener = new ActionListener(){
          
               public void actionPerformed(ActionEvent ex){
               //some code
               }
           };
           child1 = new Child1(listener);
           child2 = new Child2();
    }

}

public class Child1 extends JPanel {
    private JButton button;
    private Component component;

    public Child1(ActionListener listener){
        button = new JButton("button");
        component = new Component();
 
        button.addActionListener(listener);
        
        setUpGUI();
    }
}

Just crude coding to show what I currently do to relate sub-classes like that, I realize there are some things missing. And yes, in that above case I could've had the JButton in the main panel as it's only one button, but with JPanels or components that have many many layers to them it's not so simple and dividing them up is necessary.

Also, the Child2 panel would have a method to invoke when the action is fired, that I would call in the listener I create in the main panel.


TL;DR:

Essentially is there a better way of going about what I do in the above code to relate two sub classes that aren't in the same scope?

No correct solution

OTHER TIPS

The general strategy this is solved is through an MVC architecture. In an MVC architecture, you keep a model that represents the data being manipulated and is the single repository of truth. In some situations, this model may be linked with a database, but there does not necessarily have to be a database.

All of UI views only have to know about the model and how to update itself with regard to the current state of the model. All UI controls only have to know about the model and how to update the model when the UI control is activated. The model provides a notification service for interested parties, every time the model gets updated, the model searches through the list of interested parties for those that are listening for the type of change that had just happened, and send a change event notification so they can update themselves.

This way, any UI elements doesn't need to know about any other UI elements, it only needs to know about the models. Note that when a UI control is activated, it triggers action notifications to the model, which triggers change notifications to the UI views and possibly to the database; the UI views do not need to know what control originally triggered the change in the model and thus do not need the original action listener. Which controls triggered the change does not matter, only that the change happens and that the UI view is stale and need to be updated.

Note that a single UI widget can play the role of both a UI view and a UI controller, but these roles should be distinguished.

In your case, Child #1 seems to play the role of the UI controller, and Child #2 plays the role of one of the view that needs to be updated. You need a way to manage the events registrations such that the Model is notified when Child #1 is activated and register Child #2 as an interested party whenever the Model's status changes. Once this is set up, child #1 should only need to know to notify the Model that it wants the model to change its status to "Bye"; the Model then needs to look for who is interested in the status change, and find that Child #2 does, and sends a status change event.

There are plenty of ways to do it. Neither of them is the best (depends on the situation). I would stick to the rule called "KISS" - in short, keep it simple. If one does not have many references in their code (which is your example) - just give a reference to its sibling to each acting class, and that is it. Very easy, and you will be able to understand what is happening in your code easily. Unless you want struggling.

If you want struggling - this is a very good solution for your example: http://en.wikipedia.org/wiki/Mediator_pattern. But I would just stick to your present solution, because simplicity is the ultimate sophistication.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top