Java - Doing large scale GUI projects - java

To get right directly to my question.
How do you do large scale GUI projects. I have not done any larger GUI projects in java so far but what i am working on now grew pretty fast and pretty big and now i am stuck whit a huge pile of code that is really annoying and messy.
Since i come from field of web development i am used to MVC frameworks so i have 3 packages in my projects Model where i keep classes that interact whit files or db, Views where i keep my classes for Forms or GUI and Controller package where i keep the majority of my logic.
I have been told to separate my logic as well keep actions in one class and listeners in another class but i have no idea how to link all that up.
So far i only have 1 Controller class where i execute all the methods regarding whats happening on the GUI once its invoked.
package pft.controller;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JLabel;
import javax.swing.JComboBox;
import javax.swing.JTree;
import java.awt.event.*;
import javax.swing.JProgressBar;
import pft.view.Invoke_GUI;
import pft.model.Events;
import pft.model.Parse;
public class Tower_Controller {
public Tower_Controller() {
}
//Global variables
String isSelected = null;
int hasModules = 0;
int cap = 0;
int cpu = 0;
int shield = 0;
int armor = 0;
public void setName(String name){
this.isSelected = name;
}
public String getName(){
return this.isSelected;
}
public void setCap(int cap){
this.cap = cap;
}
public int getCap(){
return this.cap;
}
public void setCpu(int cpu){
this.cpu = cpu;
}
public int getCpu(){
return this.cpu;
}
public void setShield(int shield){
this.shield = shield;
}
public int getShield(){
return this.shield;
}
public void setArmor(int armor){
this.armor = armor;
}
public int getArmor(){
return this.armor;
}
public void invoke() throws IOException {
Invoke_GUI runnable = new Invoke_GUI();
final JLabel tower_name = runnable.tower_name;
final JComboBox tower_select = runnable.tower_select;
final JTree module_browser = runnable.module_browser;
final JTree selected_modules = runnable.selected_modules;
final JProgressBar cap_bar = runnable.cap_bar;
final JProgressBar cpu_bar = runnable.cpu_bar;
final JLabel em_res = runnable.em;
final JLabel th_res = runnable.thermic;
final JLabel ki_res = runnable.kinetic;
final JLabel ex_res = runnable.explosive;
setTowerName(tower_name, tower_select);
removeTower(tower_name);
runnable.setVisible(true);
}
public void removeTower(final JLabel tower_name) {
tower_name.addMouseListener(new MouseListener() {
#Override
public void mouseClicked(MouseEvent e) {
if (hasModules == 1 & isSelected != null) {
Events evt = new Events();
evt.towerHasModules();
} else if (isSelected == null) {
} else {
tower_name.setText("No Control Tower selected");
isSelected = null;
}
}
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
});
}
public void updateVariables(String name) throws IOException{
Parse tower = new Parse();
String data[] = tower.towerData(name);
Integer x = Integer.valueOf(data[1]).intValue();
setCap(x);
}
public void setTowerName(final JLabel tower_name, final JComboBox tower_select) {
tower_select.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (isSelected != null) {
Events evt = new Events();
evt.towerSelected(isSelected);
} else {
tower_name.setText(tower_select.getSelectedItem().toString());
setName(tower_name.toString());
try {
updateVariables(tower_name.toString());
} catch (IOException ex) {
Logger.getLogger(Tower_Controller.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
});
}
}
There are a lot of tutorials and examples how to do small usually single class Java GUI but no tutorials or examples on how to do projects that are bit larger than a single class.
Thanks in advance for all the help and
advice.

Here is my advice for Swing development in general. It does discuss the importance of using Controllers to bridge the needs of the view and the interace of the model.
GUI guidelines for swing
Last Swing project I did I designed a MVC framework that used Spring for defining the model of the program and Controllers, then used annotations in the Controller to wire up events dispatched by the view onto methods in the controller. The view had access to the event dispatcher which was an event bus, and events sent over the bus called methods on the Controller through the annotations. This allowed any Controller to respond to events from the View. So as a Controller got too large it was super simple to refactor each set of methods into another Controller, and the view or model didn't have to change.
The beauty of the event bus was it could be shared with the model as well so the model could dispatch asynchronous events the Controller could register for as well. It looked something like:
public class SomeController {
private AuthenticationModel authenticationModel;
private LoginService loginService;
private MyApp view;
#Listener( event = "login" )
public void login( LoginEvent event ) {
view.showWaitDialog();
loginService.login( event.getUserName(), event.getPassword() )
.onResult( new Callback<User>() {
public void onResult( User user ) {
authenticationModel.setUser( user );
view.hideWaitDialog();
view.showStartScreen(user);
}
});
}
}
Once this framework was in place it was amazing how fast we could get things done. And it held up pretty well as we added features. I've done my fair share of large Swing projects (3 to date), and this architecture made a huge difference.

The easiest way to scale a GUI is to make everything loosely coupled. Events (Swing's and your own) are the best way to do this. Unless a class is directly creating or showing a GUI element, it shouldn't know or care about anything else in the UI.
The Controller should continue doing what it's supposed to do - firing events in response to other events. But, these events should be application level events defined by the needs of your app. The Controller shouldn't directly manipulate GUI elements. Instead, you should create components (maybe just subclasses of JWhatever) that register themselves with the Controller as interested in events.
For example, create an TowerEventListener interface with a nameChanged() function. The Controller also has a changeTowerName() function, which when called, updates the model (a Tower class) then calls nameChanged() on all registered TowerEventListeners.
Then create a TowerRenamer class that, for example, subclasses JDialog (i.e. a popup window) that includes a text box and and OK button along with a reference to the Controller. When the user clicks OK, Controller.changeTowerName() is called. Other parts of your GUI that register as TowerEventListeners will receive the event and update as needed (maybe by updating a JLabel on the UI).
Note, if your needs are simple enough, you can just use PropertyChangeEvents and not worry about a whole event interface structure. PropertyChangeSupport can be used by the Controller to fire event notifications.

In addition to the great advice already given, I would recommend reading some of what Trygve Reenskaug has written and/or recorded on his MVC page. He was there during the development of this architectural style in the late 70's. His two page technical report entitled Models - Views - Controllers from December of 1979 presents the most concise description of the model, view, and controller.
Of particular note, views are both observers and manipulators of the model. The controller is primarily concerned with arranging (wiring) the views and translating user input into interactions with the model. Several of the MVC frameworks out there have the controller relaying data from the model to the view - this is simply wrong. A paper from earlier in 1979 included the concept of an editor as a composite of related views. The editor was discarded; its functionality was moved into both the controller and the view.
Another article that is good at describing how to apply this guideline is Burbeck's How to use Model-View-Controller. It is written with Smalltalk in mind so it might not translate to Java easily, but it is a good description of how to apply the guideline.
I think that the most important thing to consider is that the original MVC style was created for user interfaces that included more than one view (representation) of the same model. This really works well for user interfaces but does not translate exceptionally well to the web service world. Using MVC for a GUI lets you really see and understand the power of this style.

Related

In an (JavaFX) MVC architecture with a separated control, is it normal to have most your Event Handlers just calling view methods?

So I'm learning JavaFX programming and MVC. The control is also its own class and isn't integrated into the view (Which I've heard is one way to go at it). I want it to be separated from the view but because I'm trying to encapsulate everything and leave everything private with limited access to the controls/nodes, I find myself using methods to do almost anything inside of my object almost entirely when using event handlers in the control.
Example (Not an actual program, just wrote it here because I have no short examples.):
View:
public class SamplePane extends BorderPane {
private TextField tfScoreOne;
private Button btnScore, btnPenalty;
private int scoreOne;
public SamplePane() {
// Some constructor
}
public void giveScore() {
scoreOne++;
tfScoreOne.textProperty().setValue("Score: " + Integer.toString(scoreOne);
}
public void takeScore() {
scoreOne--;
tfScoreOne.textProperty().setValue("Score: " + Integer.toString(scoreOne);
}
}
public void btnScoreAddHandler(EventHandler<ActionEvent> handler) {
btnOneAdd.setOnAction(handler);
}
public void btnPenaltyAddHandler(EventHandler<ActionEvent> handler) {
btnOneAdd.setOnAction(handler);
}
Control:
public class SampleController {
public ModuleSelectionController() {
// Some contorller stuff again
samplePaneObj.btnScoreAddHandler(btnScoreHandler);
samplePaneObj.btnPenaltyAddHandler(btnScoreHandler);
}
private class btnScoreHandler implements EventHandler<ActionEvent> {
public void handle(ActionEvent arg0) {
samplePaneObj.giveScore();
}
}
private class btnPenaltyHandler implements EventHandler<ActionEvent> {
public void handle(ActionEvent arg0) {
samplePaneObj.takeScore();
}
}
}
This is mostly pseudocode so forgive me if there are any errors but do you get the point? It seems very arbitrary to just be calling methods but without passing the TextField in the example its hard to not do everything without a method doing all the work.
But is that decoupled enough for MVC? I don't really wanna break encapsulation is the main issue so I can't make the controls public and operate on them directly in the controller.
Is this all just normal? I want to make sure I'm grasping it right.
There is too much that could be said about this here. I'd advise you to have a look at a JavaFX application framework and read its documentation. I learned a lot from it. E.g., have a look here: https://github.com/sialcasa/mvvmFX
Don't make the mistake and try to derive some implementation patterns yourself from all the hello world examples out there on the internet. They all don't teach you how things should be done so that they scale well for real-world projects.

How MVC work with java swing GUI

Lets say I have a swing GUI which has textfeild and button. When I click button I want to save that value in text in db and return joptionpane "success" message.
The way I used to do this is
Model : JDBC class
View : GUI : In that button's 'action performed' action I call save method with parameter.
Controller con = new Controller();
con.save(text1.getText());
Controller : Write a save method.
JDBC db = new
public void save(jTextfeild text){
text= text1.getText();
boolean b= db.putData("insert into .. values(text)");
if(b){
JOptionPane("Success");
}
}
This is how I started. But later I understood this is not how this should be and this is utterly unsafe and stupid.
I really want to learn how to do this in MVC properly. Please be kind enough to explain this to with a small example. Thank you for your time.
This is a difficult subject to grasp in something like Swing, which already uses a form of MVC, albeit more like VC-M, where the model is separated from the view and controller, but where the view and controller are combined.
Think about a JButton, you don't supply a controller to manage how it's triggered when a user presses a key or clicks on it with the mouse, this is done internally and you are notified about the actions when the occur.
With this in mind, you need to allow the view to be semi self managed. For instance, based on your requirements, the view would have a button and text field.
The view itself would manage the interactions between the user and the button itself (maintain a internal ActionListener for example), but would then provide notifications to the controller about any state changes that the controller might be interested in.
In a more pure sense of a MVC, the view and model won't know anything about each other and the controller would manage them. This is a little contradictive to how Swing works, as Swing allows you to pass the model directly to the view, see just about any Swing component.
This doesn't mean that you can't make things work, but you need to know where the concept can falter or needs to be "massaged" to work better.
Normally, when I approach these type of things, I take step back and look at much wider picture, for example.
You have a view which can accept text and produce text or changes to it
You have a model which can load and modify text, but provides little other events
You have a controller which wants to get text from the model and supply it to the view and monitor for changes to the text by the view and update them within the model
Now, MVC works REALLY well with the concept of "code to interfaces (not implementation)", to that extent, I tend to start with the contracts...
View contract...
public interface TextView {
public void setText(String text);
public String getText();
public void addTextViewObserver(TextViewObserver observer);
public void removeTextViewObserver(TextViewObserver observer);
}
public interface TextViewObserver {
public void textWasChanged(TextView view);
}
Now, one of the requirements of the view is to generate events when the text has changed in some meaningful way, to this end, I've used a simple observer pattern to implement. Now you could argue that the controller is the observer, but to my mind, the controller may have functionality that I don't want to expose to the view (like the model for instance)
Model contract...
Next comes the model...
public interface TextModel {
public String getText();
public void setText(String text);
}
pretty simple really. Now, you might consider adding some kind of Exception to these methods to allow the model the ability to fail for some reason, but the Exception should be as generic as you can make it (or even a custom Exception), so that you can replace the implementation should you need to
Controller contract...
And finally, the controller...
public interface TextViewController {
public TextView getTextView();
public TextModel getTextModel();
}
again, pretty simple. You might have a more complex requirement for your controller, but for this example, this is about all we really need.
Implementations...
View...
public class TextViewPane extends JPanel implements TextView {
private JTextField textField;
private JButton updateButton;
private List<TextViewObserver> observers;
public TextViewPane() {
observers = new ArrayList<>(25);
textField = new JTextField(25);
updateButton = new JButton("Update");
updateButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
fireTextWasChanged();
}
});
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
add(textField, gbc);
add(updateButton, gbc);
}
#Override
public void setText(String text) {
textField.setText(text);
}
#Override
public String getText() {
return textField.getText();
}
#Override
public void addTextViewObserver(TextViewObserver observer) {
observers.add(observer);
}
#Override
public void removeTextViewObserver(TextViewObserver observer) {
observers.remove(observer);
}
protected void fireTextWasChanged() {
for (TextViewObserver observer : observers) {
observer.textWasChanged(this);
}
}
}
Model...
public class SimpleTextModel implements TextModel {
private String text = "This is some text";
#Override
public String getText() {
return text;
}
#Override
public void setText(String text) {
this.text = text;
}
}
Controller...
public class SimpleTextController implements TextViewController, TextViewObserver {
private TextView view;
private TextModel model;
public SimpleTextController(TextView view, TextModel model) {
this.view = Objects.requireNonNull(view, "TextView can not null");
this.model = Objects.requireNonNull(model, "TextModel can not be null");
view.addTextViewObserver(this);
}
#Override
public TextView getTextView() {
return view;
}
#Override
public TextModel getTextModel() {
return model;
}
#Override
public void textWasChanged(TextView view) {
getTextModel().setText(view.getText());
}
}
Putting it together...
TextViewPane view = new TextViewPane();
TextModel model = new SimpleTextModel();
TextViewController controller = new SimpleTextController(view, model);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(view);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
Now, all this is just an example of one possible solution. You could have a controller implementation which has a particular implementation of the model or view or both, for example.
The point is, you just shouldn't care. The controller doesn't care how the view is implemented, it only cares that it will generate textWasChanged events. The model doesn't care about the view at all (and visa-versa) and the controller doesn't care about model, only that it will get and set some text.
For a more complex example, you can have a look at Java and GUI - Where do ActionListeners belong according to MVC pattern?
After thoughts
This is just ONE possible way to approach the problem. For example, you could limit the view to a single observer.
You should always be thinking "can I change any one part of the MVC and will it still work?" This makes you think about the possible issues that changing any one part of the implementation might have on the surrounding contracts. You should get to the point that it simply doesn't matter how each layer is implemented
A view may act as a controller for another sub-view (or act as a container for another controller of a sub-view). This can scare people sometimes, but it's possible for a view to act as parent container for one or more sub controllers/views, this allows you to develop complex UIs
Don't expose implementation details in your contracts, for example, the model shouldn't throw a SQLException, as another implementation might not be based on a SQL based solution. Don't expose UI elements, this means that ALL implementations would then need to implement those elements. What happens if I want a implementation of the view that presents a JComboBox to the user instead of JTextField? This is also the reason I don't use a ActionListener in the view contract, because I have no idea how a textWasChanged event might actually be generated by an implementation of the view

The MVC pattern and Swing

One of the design patterns which I find most difficult to get a real grasp of in "real Swing life" is the MVC pattern. I've been through quite a few of the posts at this site which discuss the pattern, but I still do not feel that I have a clear understanding of how to take advantage of the pattern in my Java Swing application.
Let's say that I have a JFrame which contains a table, a couple of text fields and a few buttons. I would probably use a TableModel to "bridge" the JTable with an underlying data model. However, all functions responsible for clearing fields, validating fields, locking fields along with button actions would usually go directly in the JFrame. However, doesn't that mix the Controller and View of the pattern?
As far as I can see, I manage to get the MVC pattern "correctly" implemented when looking at the JTable (and the model), but things get muddy when I look at the entire JFrame as a whole.
I'd really like to hear how others go about with regard to this. How do you go about when you need to display a table, a couple of fields and some buttons to a user using the MVC pattern?
A book I'd highly recommend to you for MVC in swing would be "Head First Design Patterns" by Freeman and Freeman. They have a highly comprehensive explanation of MVC.
Brief Summary
You're the user--you interact with the view. The view is your window to the model. When you do something to the view (like click the
Play button) then the view tells the controller what you did. It's the
controller's job to handle that.
The controller asks the model to change its state. The controller takes your actions and interprets them. If you click on a
button, it's the controller's job to figure out what that means and
how the model should be manipulated based on that action.
The controller may also ask the view to change. When the controller receives an action from the view, it may need to tell the
view to change as a result. For example, the controller could enable
or disable certain buttons or menu items in the interface.
The model notifies the view when its state has changed. When something changes in the model, based either on some action you took
(like clicking a button) or some other internal change (like the next
song in the playlist has started), the model notifies the view that
its state has changed.
The view asks the model for state. The view gets the state it displays directly from the model. For instance, when the model
notifies the view that a new song has started playing, the view
requests the song name from the model and displays it. The view might
also ask the model for state as the result of the controller
requesting some change in the view.
Source (In case you're wondering what a "creamy controller" is, think of an Oreo cookie, with the controller being the creamy center, the view being the top biscuit and the model being the bottom biscuit.)
Um, in case you're interested, you could download a fairly entertaining song about the MVC pattern from here!
One issue you may face with Swing programming involves amalgamating the SwingWorker and EventDispatch thread with the MVC pattern. Depending on your program, your view or controller might have to extend the SwingWorker and override the doInBackground() method where resource intensive logic is placed. This can be easily fused with the typical MVC pattern, and is typical of Swing applications.
EDIT #1:
Additionally, it is important to consider MVC as a sort of composite of various patterns. For example, your model could be implemented using the Observer pattern (requiring the View to be registered as an observer to the model) while your controller might use the Strategy pattern.
EDIT #2:
I would additionally like to answer specifically your question. You should display your table buttons, etc in the View, which would obviously implement an ActionListener. In your actionPerformed() method, you detect the event and send it to a related method in the controller (remember- the view holds a reference to the controller). So when a button is clicked, the event is detected by the view, sent to the controller's method, the controller might directly ask the view to disable the button or something. Next, the controller will interact with and modify the model (which will mostly have getter and setter methods, and some other ones to register and notify observers and so on). As soon as the model is modified, it will call an update on registered observers (this will be the view in your case). Hence, the view will now update itself.
Not a fan of the idea that the view should be the one to be notified by the model when its data changes. I would delegate that functionality to the controller. In that case, if you change the application logic, you don't need to interfere to the view's code. The view's task is only for the applications components + layout nothing more nothing less. Layouting in swing is already a verbose task, why let it interfere with the applications logic?
My idea of MVC (which I'm currently working with, so far so good) is :
The view is the dumbest of the three. It doesn't know anything about the controller and the model. Its concern is only the swing components' prostethics and layout.
The model is also dumb, but not as dumb as the view. It performs the following functionalities.
a. when one of its setter is called by the controller, it will fire notification to its listeners/observers (like I said, I would deligate this role to the controller). I prefer SwingPropertyChangeSupport for achieving this since its already optimized for this purpose.
b. database interaction functionality.
A very smart controller. Knows the view and the model very well. The controller has two functionalities:
a. It defines the action that the view will execute when the user interacts to it.
b. It listens to the model. Like what I've said, when the setter of the model is called, the model will fire notification to the controller. It's the controller's job to interpret this notification. It might need to reflect the change to the view.
Code Sample
The View :
Like I said creating the view is already verbose so just create your own implementation :)
interface View{
JTextField getTxtFirstName();
JTextField getTxtLastName();
JTextField getTxtAddress();
}
It's ideal to interface the three for testability purposes. I only provided my implementation of Model and Controller.
The Model :
public class MyImplementationOfModel implements Model{
...
private SwingPropertyChangeSupport propChangeFirer;
private String address;
private String firstName;
private String lastName;
public MyImplementationOfModel() {
propChangeFirer = new SwingPropertyChangeSupport(this);
}
public void addListener(PropertyChangeListener prop) {
propChangeFirer.addPropertyChangeListener(prop);
}
public void setAddress(String address){
String oldVal = this.address;
this.address = address;
//after executing this, the controller will be notified that the new address has been set. Its then the controller's
//task to decide what to do when the address in the model has changed. Ideally, the controller will update the view about this
propChangeFirer.firePropertyChange("address", oldVal, address);
}
...
//some other setters for other properties & code for database interaction
...
}
The Controller :
public class MyImplementationOfController implements PropertyChangeListener, Controller{
private View view;
private Model model;
public MyImplementationOfController(View view, Model model){
this.view = view;
this.model = model;
//register the controller as the listener of the model
this.model.addListener(this);
setUpViewEvents();
}
//code for setting the actions to be performed when the user interacts to the view.
private void setUpViewEvents(){
view.getBtnClear().setAction(new AbstractAction("Clear") {
#Override
public void actionPerformed(ActionEvent arg0) {
model.setFirstName("");
model.setLastName("");
model.setAddress("");
}
});
view.getBtnSave().setAction(new AbstractAction("Save") {
#Override
public void actionPerformed(ActionEvent arg0) {
...
//validate etc.
...
model.setFirstName(view.getTxtFName().getText());
model.setLastName(view.getTxtLName().getText());
model.setAddress(view.getTxtAddress().getText());
model.save();
}
});
}
public void propertyChange(PropertyChangeEvent evt){
String propName = evt.getPropertyName();
Object newVal = evt.getNewValue();
if("address".equalsIgnoreCase(propName)){
view.getTxtAddress().setText((String)newVal);
}
//else if property (name) that fired the change event is first name property
//else if property (name) that fired the change event is last name property
}
}
The Main, where the MVC is setup :
public class Main{
public static void main(String[] args){
View view = new YourImplementationOfView();
Model model = new MyImplementationOfModel();
...
//create jframe
//frame.add(view.getUI());
...
//make sure the view and model is fully initialized before letting the controller control them.
Controller controller = new MyImplementationOfController(view, model);
...
//frame.setVisible(true);
...
}
}
The MVC pattern is a model of how a user interface can be structured.
Therefore it defines the 3 elements Model, View, Controller:
Model A model is an abstraction of something that is presented to the user. In swing you have a differentiation of gui models and data models. GUI models abstract the state of a ui component like ButtonModel. Data models abstract structured data that the ui presents to the user like TableModel.
View The view is a ui component that is responsible for presenting data to the user. Thus it is responsible for all ui dependent issues like layout, drawing, etc. E.g. JTable.
Controller A controller encapsulates the application code that is executed in order to an user interaction (mouse motion, mouse click, key press, etc.). Controllers might need input for their execution and they produce output. They read their input from models and update models as result of the execution. They might also restructure the ui (e.g. replace ui components or show a complete new view). However they must not know about the ui compoenents, because you can encapsulate the restructuring in a separate interface that the controller only invokes. In swing a controller is normally implemented by an ActionListener or Action.
Example
Red = model
Green = view
Blue = controller
When the Button is clicked it invokes the ActionListener. The ActionListener only depends on other models. It uses some models as it's input and others as it's result or output. It's like method arguments and return values. The models notify the ui when they get updated. So there is no need for the controller logic to know the ui component. The model objects don't know the ui. The notification is done by an observer pattern. Thus the model objects only know that there is someone who wants to get notified if the model changes.
In java swing there are some components that implement a model and controller as well. E.g. the javax.swing.Action. It implements a ui model (properties: enablement, small icon, name, etc.) and is a controller because it extends ActionListener.
A detailed explanation, example application and source code: https://www.link-intersystems.com/blog/2013/07/20/the-mvc-pattern-implemented-with-java-swing/.
MVC basics in less than 260 lines:
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.DefaultListModel;
import javax.swing.DefaultListSelectionModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.WindowConstants;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.PlainDocument;
public class Main {
public static void main(String[] args) {
JFrame mainFrame = new JFrame("MVC example");
mainFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
mainFrame.setSize(640, 300);
mainFrame.setLocationRelativeTo(null);
PersonService personService = new PersonServiceMock();
DefaultListModel searchResultListModel = new DefaultListModel();
DefaultListSelectionModel searchResultSelectionModel = new DefaultListSelectionModel();
searchResultSelectionModel
.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
Document searchInput = new PlainDocument();
PersonDetailsAction personDetailsAction = new PersonDetailsAction(
searchResultSelectionModel, searchResultListModel);
personDetailsAction.putValue(Action.NAME, "Person Details");
Action searchPersonAction = new SearchPersonAction(searchInput,
searchResultListModel, personService);
searchPersonAction.putValue(Action.NAME, "Search");
Container contentPane = mainFrame.getContentPane();
JPanel searchInputPanel = new JPanel();
searchInputPanel.setLayout(new BorderLayout());
JTextField searchField = new JTextField(searchInput, null, 0);
searchInputPanel.add(searchField, BorderLayout.CENTER);
searchField.addActionListener(searchPersonAction);
JButton searchButton = new JButton(searchPersonAction);
searchInputPanel.add(searchButton, BorderLayout.EAST);
JList searchResultList = new JList();
searchResultList.setModel(searchResultListModel);
searchResultList.setSelectionModel(searchResultSelectionModel);
JPanel searchResultPanel = new JPanel();
searchResultPanel.setLayout(new BorderLayout());
JScrollPane scrollableSearchResult = new JScrollPane(searchResultList);
searchResultPanel.add(scrollableSearchResult, BorderLayout.CENTER);
JPanel selectionOptionsPanel = new JPanel();
JButton showPersonDetailsButton = new JButton(personDetailsAction);
selectionOptionsPanel.add(showPersonDetailsButton);
contentPane.add(searchInputPanel, BorderLayout.NORTH);
contentPane.add(searchResultPanel, BorderLayout.CENTER);
contentPane.add(selectionOptionsPanel, BorderLayout.SOUTH);
mainFrame.setVisible(true);
}
}
class PersonDetailsAction extends AbstractAction {
private static final long serialVersionUID = -8816163868526676625L;
private ListSelectionModel personSelectionModel;
private DefaultListModel personListModel;
public PersonDetailsAction(ListSelectionModel personSelectionModel,
DefaultListModel personListModel) {
boolean unsupportedSelectionMode = personSelectionModel
.getSelectionMode() != ListSelectionModel.SINGLE_SELECTION;
if (unsupportedSelectionMode) {
throw new IllegalArgumentException(
"PersonDetailAction can only handle single list selections. "
+ "Please set the list selection mode to ListSelectionModel.SINGLE_SELECTION");
}
this.personSelectionModel = personSelectionModel;
this.personListModel = personListModel;
personSelectionModel
.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
ListSelectionModel listSelectionModel = (ListSelectionModel) e
.getSource();
updateEnablement(listSelectionModel);
}
});
updateEnablement(personSelectionModel);
}
public void actionPerformed(ActionEvent e) {
int selectionIndex = personSelectionModel.getMinSelectionIndex();
PersonElementModel personElementModel = (PersonElementModel) personListModel
.get(selectionIndex);
Person person = personElementModel.getPerson();
String personDetials = createPersonDetails(person);
JOptionPane.showMessageDialog(null, personDetials);
}
private String createPersonDetails(Person person) {
return person.getId() + ": " + person.getFirstName() + " "
+ person.getLastName();
}
private void updateEnablement(ListSelectionModel listSelectionModel) {
boolean emptySelection = listSelectionModel.isSelectionEmpty();
setEnabled(!emptySelection);
}
}
class SearchPersonAction extends AbstractAction {
private static final long serialVersionUID = 4083406832930707444L;
private Document searchInput;
private DefaultListModel searchResult;
private PersonService personService;
public SearchPersonAction(Document searchInput,
DefaultListModel searchResult, PersonService personService) {
this.searchInput = searchInput;
this.searchResult = searchResult;
this.personService = personService;
}
public void actionPerformed(ActionEvent e) {
String searchString = getSearchString();
List<Person> matchedPersons = personService.searchPersons(searchString);
searchResult.clear();
for (Person person : matchedPersons) {
Object elementModel = new PersonElementModel(person);
searchResult.addElement(elementModel);
}
}
private String getSearchString() {
try {
return searchInput.getText(0, searchInput.getLength());
} catch (BadLocationException e) {
return null;
}
}
}
class PersonElementModel {
private Person person;
public PersonElementModel(Person person) {
this.person = person;
}
public Person getPerson() {
return person;
}
#Override
public String toString() {
return person.getFirstName() + ", " + person.getLastName();
}
}
interface PersonService {
List<Person> searchPersons(String searchString);
}
class Person {
private int id;
private String firstName;
private String lastName;
public Person(int id, String firstName, String lastName) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
}
public int getId() {
return id;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
}
class PersonServiceMock implements PersonService {
private List<Person> personDB;
public PersonServiceMock() {
personDB = new ArrayList<Person>();
personDB.add(new Person(1, "Graham", "Parrish"));
personDB.add(new Person(2, "Daniel", "Hendrix"));
personDB.add(new Person(3, "Rachel", "Holman"));
personDB.add(new Person(4, "Sarah", "Todd"));
personDB.add(new Person(5, "Talon", "Wolf"));
personDB.add(new Person(6, "Josephine", "Dunn"));
personDB.add(new Person(7, "Benjamin", "Hebert"));
personDB.add(new Person(8, "Lacota", "Browning "));
personDB.add(new Person(9, "Sydney", "Ayers"));
personDB.add(new Person(10, "Dustin", "Stephens"));
personDB.add(new Person(11, "Cara", "Moss"));
personDB.add(new Person(12, "Teegan", "Dillard"));
personDB.add(new Person(13, "Dai", "Yates"));
personDB.add(new Person(14, "Nora", "Garza"));
}
public List<Person> searchPersons(String searchString) {
List<Person> matches = new ArrayList<Person>();
if (searchString == null) {
return matches;
}
for (Person person : personDB) {
if (person.getFirstName().contains(searchString)
|| person.getLastName().contains(searchString)) {
matches.add(person);
}
}
return matches;
}
}
You can create model in a separate, plain Java class, and controller in another.
Then you can have Swing components on top of that. JTable would be one of the views (and table model would de facto be part of the view - it would only translate from the "shared model" to JTable).
Whenever the table is edited, its table model tells the "main controller" to update something. However, the controller should know nothing about the table. So the call should look more like: updateCustomer(customer, newValue), not updateCustomer(row, column, newValue).
Add a listener (observer) interface for the shared model. Some components (e.g. your table) could implement it directly. Another observer could be the controller that coordinates button availability etc.
That's one way to do it, but of course you can simplify or extend it if its an overkill for your use case.
You can merge the controller with model and have the same class process updates and maintain component availability. You even can make the "shared model" a TableModel (though if it's not only used by the table, I would recommend at least providing a friendlier API that doesn't leak table abstractions)
On the other hand, you can have complex interfaces for updates (CustomerUpdateListener, OrderItemListener, OrderCancellationListener) and dedicated controller (or mediator) only for coordination of different views.
It depends on how complicated your problem is.
For proper separation, you would typically have a controller class that the Frame class would delegate to. There are various ways to set up the relationships between the classes - you could implement a controller and extend it with your main view class, or use a standalone controller class that the Frame calls when events occur. The view would typically receive events from the controller by implementing a listener interface.
Sometimes one or more parts of the MVC pattern are trivial, or so 'thin' that it adds unnecessary complexity to separate them out. If your controller is full of one line calls, having it in a separate class can end up obfuscating the underlying behaviour. For instance, if the all of the events you are handling are related to a TableModel and are simple add and delete operations you might choose to implement all of the table manipulation functions within that model (as well as the callbacks necessary to display it in the JTable). It's not true MVC, but it avoids adding complexity where it isn't needed.
However you implement it, remember to JavaDoc your classes, methods and packages so that the components and their relationships are properly described!
I have found some interesting articles about implementing MVC Patterns, which might solve your problem.
http://www.oracle.com/technetwork/articles/javase/index-142890.html
http://accu.org/index.php/journals/1524
If you develop a program with a GUI, mvc pattern is almost there but blurred.
Disecting model, view and controller code is difficult, and normally is not only a refactor task.
You know you have it when your code is reusable. If you have correctly implemented MVC, should be easy to implement a TUI or a CLI or a RWD or a mobile first design with the same functionality. It's easy to see it done than do it actually, moreover on an existing code.
In fact, interactions between model, view and controller happens using other isolation patterns (as Observer or Listener)
I guess this post explains it in detail, from the direct non MVC pattern (as you will do on a Q&D) to the final reusable implementation:
http://www.austintek.com/mvc/

progress bars + MVC in Java =?

So I have this nice spiffy MVC-architected application in Java Swing, and now I want to add a progress bar, and I'm confused about Good Design Methods to incorporate a JProgressBar into my view. Should I:
add a DefaultBoundedRangeModel to my controller's state, and export it?
class Model {
final private DefaultBoundedRangeModel progress
= new DefaultBoundedRangeModel();
public void getProgressModel() { return progress; }
public void setProgressCount(int i) { progress.setValue(i); }
}
class Controller {
Model model;
int progressCount;
void doSomething()
{
model.setProgressCount(++progressCount);
}
}
class View {
void setup(Model m)
{
JProgressBar progressBar = /* get or create progress bar */ ;
progressBar.setModel(m.getProgressModel());
}
}
/* dilemma: Model allows progress to be exported so technically
all of the progress state could be set by someone else; should it be put
into a read-only wrapper? */
use JGoodies Binding to try to connect the JProgressBar's visual state to my model's state?
class Model {
private int progress;
public void getProgressCount() { return progress; }
public void setProgressCount(int i) { progress = i; }
}
class View {
void setup(Model m)
{
ProgressBar progressBar = /* get or create progress bar */ ;
CallSomeMagicMethodToConnect(m, "progressCount", progressBar, "value");
// is there something that works like the above?
// how do I get it to automatically update???
}
}
or something else???
edit: more specifically: could someone point me to a Good Example of realistic source for an application in Java that has a status bar that includes a progress bar, and has a decent MVC implementation of it?
No (to 1) and NOOOO (to 2). At least in my opinion.
No (to 1): First, DefaultBoundedRangeModel is a javax.swing class. In my opinion, these classes have no place in models. For example, think about the model living on the server, being accessed via RMI - All of the sudden putting a javax.swing class there seems "not right".
However, the real problem is that you're giving a part of your model (the bounded model) to someone else, with no control over events fired or queries made.
No (to 2): Ugh. Binding is fun but (at least in my opinion) should be used to synchronize between UI model and UI components, not between data model and UI model. Again, think what would happen if your data model lived on a remote server, accessed by RMI.
So what? Well, this is only a suggestion, but I'd add an event listener interface and add the standard event listener subscription methods (addListner(...), removeListener(...)). I'd call these listeners from within my model when I have updates going on. Of course, I'd make sure to document the calling thread (or say it cannot be determined) in order for the client (the UI in this case) to be able to synchronize correctly (invokeLater and friends). Since the listener service will be exposed by the controller, this will allow the model to live anywhere (even allowing for listeners to be remotely invoked or pooled). Also, this would decouple the model from the UI, making it possible to build more models containing it (translators / decorators / depending models).
Hope this helps.
I would say, something else.
The problem I have had with MVC, is to define the level of abstraction of the model.
Model could be some sort of objects for the UI components
Model could also be some other sort of objects for the program it self.
and
Model could be as high as business models.
In this case I would have separated model/component pairs for the progress bar and handle them in a separate controller class.
This article describes swing architecture and might clarify the way it uses models inside.
In our app (MVC, about 100 KLOC) we have it like that (pattern Observer, actually):
/**
* Observer on progress changes
*/
public interface IProgressListener {
public void setProgress(ProgressEvent e);
}
public class ProgressEvent extends ... {
private int progressCount;
// setter + getter
...
}
class Model {
public void addProgressListener(IProgressListener l);
protected void fireProgressChange(ProgressEvent e); // call .setProgress() on listeners
}
class Controller {
private Model model;
}
class View extends ProgressBar implements IProgressListener {
...
// IProgressListener implementation
public void setProgress(ProgressEvent e) {
this.setValue(e.getProgress());
}
...
}

Generic, annotation-driven event notification frameworks

While simple, interface-driven event notification frameworks in Java have been around since pre-Cambrian times (e.g. java.beans.PropertyChangeSupport), it is becoming increasingly popular for frameworks to use annotation-driven event notification instead.
For an example, see JBossCache 2.2. The listener class has its listener methods annotated, rather than conforming to a rigid interface. This is rather easier to program to, and easier to read, since you don't have to write empty implementations of listener callbacks that you're not interested in (and yes, I know about listener adapter superclasses).
Here's a sample from the JBossCache docs:
#CacheListener
public class MyListener {
#CacheStarted
#CacheStopped
public void cacheStartStopEvent(Event e) {
switch (e.getType()) {
case Event.Type.CACHE_STARTED:
System.out.println("Cache has started");
break;
case Event.Type.CACHE_STOPPED:
System.out.println("Cache has stopped");
break;
}
}
#NodeCreated
#NodeRemoved
#NodeVisited
#NodeModified
#NodeMoved
public void logNodeEvent(NodeEvent ne) {
log("An event on node " + ne.getFqn() + " has occured");
}
}
The problem with this, is that it's very much more of an involved process writing the framework to support this sort of thing, due to the annotation-reflection nature of it.
So, before I charge off down the road of writing a generic framework, I was hoping someone had done it already. Has anyone come across such a thing?
You can already do this today with EventBus.
Following example is from EventBus Getting Started guide. Statusbar that updates based on published events, and no need to register statusbar control/widget as listener of publisher(s). Without EventBus, statusbar will need to be added as listener to many classes. Statusbar can also be created and destroyed at any time.
public StatusBar extends JLabel {
public StatusBar() {
AnnotationProcessor.process(this);
}
#EventSubscriber(eventClass=StatusEvent.class)
public void updateStatus(StatusEvent statusEvent) {
this.setText(statusEvent.getStatusText();
}
}
A similar project is ELF (Event Listener Framework) but it seems to be less mature.
I'm currently researching about event notification frameworks on Publish-Subscribe Event Driven Programming | Kev's Spring vs Java EE Dev and the followup articles.
I've made http://neoevents.googlecode.com to handle this kind of annotation based event handler.
#actionPerformed
private void onClick() {
//do something
}
protected void initComponents() {
JButton button = new JButton("Click me!!!");
button.addActionListener(new ActionListener(this) );
}
It looks as simple as I was expecting it to be. Annotations are available for every single listener in J2SE.
Don't mistake complicated for clever. It seems to me that this would be:
A nightmare to debug
Difficult to follow (from a maintenance perspective, or someone attempting to change something 6 months down the line)
Full of if (event instanceof NodeCreatedEvent) like code. Why this is better than subclassing an adapter I have no idea!
The main problem I see here are the method parameters, which restrict which methods can actually be used for which events, and there's no compile-time help for that.
This is what makes interfaces attractive to me for observer pattern implementations like the Java event model. Tools like eclipse can autogen method stubs so you can't get the signatures wrong. In your example, it's very easy to use the wrong parameter type and never know it until an event occurs (which might be an error case several months down the line)
One thing you might try are my annotations & processor for implementing observers and null object implementations. Suppose you have
package a.b.c;
public interface SomeListener {
void fee();
void fie();
void fo();
void fum();
}
and wanted to create a listener instance. You could write
package x.y.z;
import a.b.c.SomeListener;
import com.javadude.annotation.Bean;
import com.javadude.annotation.NullObject;
#Bean(nullObjectImplementations = {#NullObject(type = SomeListener.class) })
public class Foo extends FooGen implements SomeListener {
#Override
public void fie() {
// whatever code you need here
}
}
To create a source for these events, you can write
package a.b.c;
import com.javadude.annotation.Bean;
import com.javadude.annotation.Observer;
#Bean(observers = {#Observer(type = SomeListener.class)})
public class Source extends SourceGen {
// SourceGen will have add/remove listener and fire methods
// for each method in SomeListener
}
See http://code.google.com/p/javadude/wiki/Annotations if you're interested. Might give you some other ideas as well.
Google Guava v11 has added an EventBus component that uses this style. They also explain why they decided to use annotations rather than interfaces.
I've been thinking about a generic annotation-driven event framework as well. I like the benefits provided by static typing, but the current interface-driven event model is painful to use (ugly code). Would it be possible to use a custom annotation processor to do some compile-time checking? That might help add some of the missing "safety" that we've all grown used to.
A lot of the error checking can also be done at the time that the listeners are "registered" with the event producers. Thus, the application would fail early (when the listeners are registered), possibly even at at startup-time.
Here's an example of what the generic framework I've been toying with might look like:
public class ExampleProducer {
private EventSupport<ActionEvent> eventSupport;
public ExampleProducer() {
eventSupport = new EventSupport<ActionEvent>(this);
}
#AddListenersFor(ActionEvent.class)
public void addActionListener(Object listener)
{
eventSupport.addListener(listener);
}
#RemoveListenersFor(ActionEvent.class)
public void removeActionListener(Object listener)
{
eventSupport.removeListener(listener);
}
public void buttonClicked() {
eventSupport.fire(new ActionEvent(this,
ActionEvent.ACTION_PERFORMED, "Click"));
}
}
The producer uses EventSupport, which uses reflection to invoke the events. As mentioned before, EventSupport could preform some initial checks when the events listeners are registered.
public class ExampleListener
{
private ExampleProducer submitButton;
public ExampleListener()
{
submitButton = new ExampleProducer();
EventSupport.autoRegisterEvents(this);
}
#HandlesEventFor("submitButton")
public void handleSubmitButtonClick(ActionEvent event)
{
//...some code to handle the event here
}
}
Here, EventSupport has a static method that uses reflection to auto-register the listener with the event producer. This eliminates the need to manually register with the event source. A custom annotation processor could be used to validate that the #HandlesEventFor annotation refers to an actual field of the ExampleListener. The annotation processor could do other checks as well, such as ensuring that the event handler method signature matches up with one of the registration methods on the ExampleProducer (basically, the same check that could be performed at registration-time).
What do you think? Is this worth putting some time into fully developing?
Here's a similar project called SJES.
public class SomeController {
private Calculator c1 = new Calculator();
private Calculator c2 = new Calculator();
public SomeController() {
c1.registerReceiver(this);
c2.registerReceiver(this);
c1.add(10, 10);
c2.add(20, 20);
}
#EventReceiver(handleFor="c1")
public void onResultC1(Calculator.Event e) {
System.out.println("Calculator 1 got: " + e.result);
}
#EventReceiver(handleFor="c2")
public void onResultC2(Calculator.Event e) {
System.out.println("Calculator 2 got: " + e.result);
}
#EventReceiver
public void onResultAll(Calculator.Event e) {
System.out.println("Calculator got: " + e.result);
}
}
public class Calculator {
private EventHelper eventHelper = new EventHelper(this);
public class Event {
long result;
public Event(long result) {
this.result = result;
}
}
public class AddEvent extends Event {
public AddEvent(long result) {
super(result);
}
}
public class SubEvent extends Event {
public SubEvent(long result) {
super(result);
}
}
public void unregisterReceiver(Object o) {
eventHelper.unregisterReceiver(o);
}
public void registerReceiver(Object o) {
eventHelper.registerReceiver(o);
}
public void add(long a, long b) {
eventHelper.fireEvent(new AddEvent(a + b));
}
public void sub(long a, long b) {
eventHelper.fireEvent(new SubEvent(a - b));
}
public void pass(long a) {
eventHelper.fireEvent(new Event(a));
}
}
I think this is very easy to use.
You can also check out MBassador It is annotation driven, very light-weight and uses weak references (thus easy to integrate in environments where objects lifecycle management is done by a framework like spring or guice or somethign).
It provides an object filtering mechanism (thus you could subscribe to NodeEvent and attach some filters to restrict message handling to a set of specific types only).
You can also define your own annotations to have customized declaration of your handlers.
And it's very fast and resource efficient. Check out this benchmark showing a performance graph for different scenarios using Guava or mbassador.

Categories