I am making a simple business application in java. The application is based on saving clients and when the user needs to see the data of each one, JTree
select it in one and display one JPanel
with their data. To do it I am using the pattern MVC
and here comes the problem, my view needs two types of Listeners
, one of type ActionListener
and another of type TreeSelectionListener
. Now, what I usually do is that I add a parameter to the constructor to my main view (or sometimes I do it in a setter) aActionListener
but now that I have two I don't want to have to depend on two parameters to instantiate my view, I think there must be a better way to do it, eg: Some factory class, some helper class (with static variables) but, before getting hands on code, I want to know what would be the best option.
CODE:
public class ApplicationWindow extends JFrame {
private List<Cliente> clientes = new LinkedList<>();
private ActionListener actionListener;
//Panels
private ClientTree clientTree;
private CenterPane centerPane;
//Menu
private JMenuBar menuBar = new JMenuBar();
private JMenuItem fileMenuItem = new JMenuItem("File");
private JMenuItem editMenuItem = new JMenuItem("Edit");
private JMenuItem viewMenuItem = new JMenuItem("View");
public ApplicationWindow(ActionListener actionListener) {
this.actionListener = actionListener;
setLayout(new BorderLayout());
//Window
this.setBounds(new Rectangle(0, 0, 600, 400));
this.setVisible(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Left pane.
clientTree = new ClientTree(clientes,actionListener);
//Center pane.
centerPane = new CenterPane();
centerPane.setBackground(Color.red);
//Add menuBar
menuBar.add(fileMenuItem);
menuBar.add(editMenuItem);
menuBar.add(viewMenuItem);
//Add
add(centerPane, BorderLayout.CENTER);
add(clientTree,BorderLayout.WEST);
add(menuBar, BorderLayout.NORTH);
}
public void updateClients(List<Cliente> newClientes) {
clientes = newClientes;
}
public void setClientes(List<Cliente> clientes) {
this.clientes = clientes;
}
public void setActionListener(ActionListener actionListener) {
this.actionListener = actionListener;
}
}
This is the code for the main window. In case I'm not explaining myself well, I need a ActionListener
(which I already have) and also a TreeSelectionListener
but I don't want to have to depend on 2 Listeners
in my constructor or remember that before using my window, I need to set them. So the question would be some (correct) way where I just configure it and don't depend on setting them or a larger constructor.
As much
ActionListener
asTreeSelectionListener
they are interfaces that extend the interfaceEventListener
. By virtue of this inheritance (Class A extends class C or class B implements class C), it is possible to pass as arguments to a method that expects an object that is an instance of class C any object that is a subtype of class C (in this case, an instance of class A or class B).It is possible to implement your class's constructor code
ApplicationWindow
in such a way that one of its arguments is an object of typeEventListener
. This way, client code that calls your code can, when invoking the constructor, pass it a variable of typeActionListener
or of typeTreeSelectionListener
.At runtime, however, you'll need to modify the constructor's implementation to check whether the sent object is a subtype of
ActionListener
orTreeSelectionListener
. This is because, although they inherit fromEventListener
, they are still interfaces and therefore the methods that your classClientTree
uses from these listeners are completely different and only the objects that they implementActionListener
areTreeSelectionListener
known.Now, if the problem is that you must be able to receive both listeners and use both at the same time, you need to modify the constructor to either receive both listeners as two different parameters or an array of type listeners
EventListener
where the client code is in charge of instantiate these objects.The previous solution makes sense since the constructor is actually supplying the listeners to the class
ClientTree
in the constructor and this is the one who will ultimately decide under which events it will invoke the corresponding listeners (it is not possible to give more information about it because the implementation of this class). On the other hand:passing an array of type EventListener in the constructor argument allows for scalability in the number of objects of this type that can be used, but the same inconvenience will occur: since the two implementations of objects of type
ActionListener
and are very differentTreeSelectionListener
, it would be very Naturally, this separation of concerns was well defined in the implementation of the ClientTree class. In this sense, any design pattern that uses inheritance as a solution does not make sense, because if the final implementations of these interfaces have a completely different API, it would not be possible to take advantage of the polymorphism that is achieved by virtue of inheritance.If you use dependency injection you don't need to initialize the listener in the constructor. For example, using Spring , your attribute would look like this: