I'm currently programming a Blackjack application in Java and it needs to be written in an MVC architecture. I have all my models, views and controllers fully coded and everything works perfectly. However, I'm wondering what the best way of attaching listeners to components is (e.g, a JButton).
My current design declares all components in the view that need listeners as public final. It then passes the view to the controller. From there, the controller has full reign to access the components and add action listeners. Code example:
public class View extends JFrame {
public final JButton myBtn1 = new JButton("Button 1");
public final JButton myBtn2 = new JButton("Button 2");
public View() {
//constructor code here....
}
}
public class Controller {
private Model myModel;
private View myView;
public Controller(Model m, View v) {
myModel = m;
myView = v;
//now add listeners to components...
myView.myBtn1.addActionListener(new MyActionListener1());
myView.myBtn2.addActionListener(new MyActionListener2());
}
}
However, one of my friends has declared all his JButton's with private scope and then made public methods in the view to add action listeners to their respective components. To me, this just seems like a waste of time and only adds unnecessary code. Code example:
public class View extends JFrame {
private JButton myBtn1 = new JButton("Button 1");
private JButton myBtn2 = new JButton("Button 2");
public View() {
//constructor code here....
}
public void myBtn1AddActionListener(ActionListener a) {
myBtn1.addActionListener(a);
}
public void myBtn2AddActionListener(ActionListener a) {
myBtn2.addActionListener(a);
}
//etc etc...
}
public class Controller {
private Model myModel;
private View myView;
public Controller(Model m, View v) {
myModel = m;
myView = v;
//now add listeners to components...
myView.myBtn1AddActionListener(new MyActionListener1());
myView.myBtn2AddActionListener(new MyActionListener2());
}
}
So, given the two scenarios above, which is the better way?
Thanks.
Note: this answer is my personal opinion. I still need to find decent literature on the "best/most recommended" way to write Swing UI and tie it to business logic. But until I find that, I use common sense and personal experience.
I would say neither of the two is the correct way to implement this. In MVC, the view is simply the part where you provide the visual information to the user. However, the code which is triggered in the ActionListener
when you press the button is code which belongs in the controller as it is most likely business logic (this is an assumption I make based on your code snippet. If your button just performs a UI action like enabling another component the action listener should be completely contained in the view).
So I would make those methods public in the controller, e.g. if you currently have an ActionListener
like
public void actionPerformed( ActionEvent e ){
doSomeStuff( UI info, event info );
}
where UI info
is info obtained from the view and event info
info obtained from the event, I would introduce the public method on the controller
public void doSomeStuff( UI info, event info )
and call that method from the view. This has 2 major benefits:
- There is only a dependency from the view onto the controller, instead of one from the controller to the view. If the controller contains business logic, it is not unlikely that you want to reuse that code for more the a SwingUI. Having a dependency on Swing classes is unwanted here. An example can be unit testing. If your controller does not depend on the UI, you can easily test the controller-model part of your application, and follow the same code paths by using the API as you would when invoking those calls through the UI.
- You can adjust your UI completely and decide to replace the button by another component to which you cannot attach an
ActionListener
without having to rewrite your controller and your view.
Edit
Although I think the above part of my post is still valid, I must admit that after reading the Wikipedia MVC page referred to in trashgod's answer (which he mentioned in one of the comments), the above is only one interpretation of the MVC pattern and how to implement this in a Swing application.
Looking at that Wiki page, it defines the three components in MVC as (summarized):
- Model: manages the behavior and data of the application domain
- View: renders the model into a form suitable for interaction, typically a user interface element
- Controller: receives user input and initiates a response by making calls on model objects
My interpretation/opinion/preferred way is to have one model and one controller, and if needed multiple views (or for example in testing zero views). The controller does not receives user input directly (which would be the equivalent of letting it register listeners on the view), but offers API hooks to pass the user input into. This provide a sort of 'abstract controller' which can handle user input, but the view still needs to translate the real user input events into events/information understandable by the controller. So this means I can as easily create a testing 'view' side of the MVC as well as a real 'view' side without having to change anything in my controller or model.
Another interpretation would be to have a more tight coupling between controller and view (where they really depend on each other). This would also mean that if you decide to switch from a Swing UI view to a command-line view you probably have to change the controller as well. Is this better/worse ... I think that is a matter of personal taste.
The only thing that was not so good in my original answer is that I located business logic in the controller part, while the Wiki page clearly states it is located in the model. I got probably confused by some simply Ruby on Rails experiments where the model was nothing more then a Data Access Layer. My bad ...
My current design declares all components in the view that need listeners as public final. It then passes the view to the controller. From there, the controller has full reign to access the components and add action listeners. Code example:
No, do not expose fields needlessly -- you should not sacrifice encapsulation for the sake of MVC. You should not be making components public for the reasons you state above. Instead give the view public methods that the control can call, or have the view change states via listeners to the model. Pass the control to the view and the view to the control, and all interactions should be through public methods, not through exposing components or fields.
One way I've solved this, and I'm not saying that it is the best or canonical way to do this is to pass an instance of Control into View and use anonymous inner classes for my listeners and have these classes call control methods. So for instance,
// inside of view
exitButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
if (control != null) {
control.exitAction();
}
}
});
Though I personally like your style, you will find that your friend's style (or similar) is more "standard Java-like". So, in a typically programming team, it will be more acceptable. It also has the (possible) advantage (and possible gotchas) that the buttons could be replaced by different buttons in certain use-cases.
If your question related to architecture API (currently not real API) then I think both ways are bad, moreover DO NOT USE any patterns before you will not have real experience and clear understand it, because result will be not good.