i have a question regarding a program i am working on. It`s a database manager for a MqSQL db , written in Java. So i have the following program structure.
So i have a main class that extends JFrame, whichh is the main frame of the interface, like this (removed the unecessary code not relevant to the discussion) :
public class MainInterface extends JFrame {
public MainInterface {
................
MainInterface.setLayout(new FlowLayout());
MainInterface.setVisible(true);
TopToolbar toolbar;
try {
toolbar = new TopToolbar();
MainInterface.add(toolbar);
ResultsPanel Results = new ResultsPanel();
MainInterface.add(Results);
} catch (IOException e) {
e.printStackTrace();
}
}
TopToolbar and ResultsPanel are 2 other classes that extend JPanel, the TopToolbar class having a JToolBar with buttonsadded to it (Move Forward, MoveBack, Add entry)
public class TopToolbar extends JPanel implements ActionListener {
TopToolBar()
{
//constructor in which i was adding button to the toolbar, not relevat
}
}
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
if (MoveFirst.equals(cmd)){
try {
DatabaseAccess disp = new DatabaseAccess();
disp.getResults();
int id = disp.return_id();
System.out.println(id);
} catch (//exceptions) {
e1.printStackTrace();
}
}
That is the ActionListener event for the next button, which should trigger reading the next entry in the db
DatabaseAccess is another class with initializes the DB connection , and has these 2 methods :
public void getResults() throws SQLException {
Connection con = (Connection) DriverManager.getConnection(URL, user, "")
sql = (Statement) con.createStatement();
result_set = sql.executeQuery("select * from persons");
while (result_set.next()) {
id = result_set.getInt(1);
name = result_set.getString(2);
}
}
public int return_id(){
return id;
}
The return_ID method returns (and it does work) the ID (first key in the database, will obviously add methods for the rest of the entries in the db).
Now i want to show the ID in the final JPanel, the one called ResultsSet (just 3 JLabels and 3 TextFields for the ID , Name etc., in a GridLayout).
Since the dababase class creation (and subsequently the methods forr reading the db and returning the result) is done inside the ActionPerformed method inside the TopToolBar Jpanel, i can`t access it from the MainInterface JFrame and then use something like
ResultsPanel DispResults = new ResultsPanel();
add(DispResults);
DispResults.setID(id_Value)
where setID would be a method in the ResultsPanel that uses the JTextBox setText to set the text .
Hoope i`ve managed to explain my issue as clear as i can.
I disagree with several of your choices.
You should not extend JFrame. Create a JPanel and give it to a JFrame to display.
I would dissociate the database interactions from the Swing UI. Create them as interface-based POJOs on their own, without a UI. Get them tested, written, and working perfectly.
Once the database interactions are perfect, give an instance of that object to the class that controls the application. Give it a text-only interface for driving the database actions.
Once the controller is obeying every text command perfectly, using your fully tested database component, then have it create an instance of your JPanel and give it to a JFrame. It will make the same calls to its controller owner that your text UI has already executed successfully.
Computer science is all about decomposition. You solve big problems by breaking them into smaller ones.
If you'd like to see a great example of what your app might look like, download SQL Squirrel.
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
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.
I am currently developing my own minesweeper. Swing follows Model-View-Controller design pattern. In MVC, I learnt whenever there is a change in model, the controller will trigger that change in view too. But In this example, I cannot trace how to make the changes in setTitle and setInfo to get reflected in view.
Here, when I set the title of the Dialog box, the actual content(model) is getting changed, But there is no corresponding change in the output(view).
//InfoDisplayer is inner class of class MenuActionListener
class InfoDisplayer extends JDialog {
JLabel info;
BorderLayout infoBorderLayout = new BorderLayout();
public InfoDisplayer(JFrame ownerFrame) {
super(ownerFrame,true);
info = new JLabel();
setFocusable(false);
setSize(300,400);
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
setLayout(infoBorderLayout);
add(info,BorderLayout.SOUTH);
setVisible(true);
}
void setInfo(JLabel info) {
this.info = info;
}
public void setTitle(String title) {
super.setTitle(title);
}
}
if ((event.getActionCommand()).equals("HowToPlay")) {
InfoDisplayer instructionsDisplay = new InfoDisplayer(gUIManagerFrame);
//gUIManagerFrame is an object of its outer class,MenuActionListener
instructionsDisplay.setTitle("INSTRUCTIONS");
instructionsDisplay.setInfo(new JLabel("<html><h1><B>INSTRUCTIONS</B></h1></html>"));
} else {// if about is clicked!!
InfoDisplayer aboutDisplay = new InfoDisplayer(gUIManagerFrame);
aboutDisplay.setTitle("MineSweeper v0.1");
aboutDisplay.setInfo(new JLabel("<html><h1><B>MineSweeperv1.0</B></h1> </html>"));
}
Whenever there is a change in model, the controller will trigger that change in view.
In the Model–View–Controller pattern, when the controller updates the model, the model will notify the view, typically using the observer pattern, and the view then updates itself. The view may interrogate the model and process any resulting update. There's a more detailed answer and example here.
You will need to remove the old jlabel and add the new one to the frame.
Though it would make more sense probably to set the text on the existing label rather than a whole new label.
Swing indeed has a model and a view side. For example in a JTable the JTable is the view and the TableModel is the model. When you construct a JTable, you need to pass it a model either during construction or by using the setter. The JTable will then add a listener to model to get informed about any model changes. You can see this listener as the controller.
However, this does not mean that when you use an arbitrary combination of Swing classes they will auto-magically get informed about each other changes. In your case, the label is certainly not 'the model' of your dialog, and there is no such thing as a 'controller' between your label and the dialog. When you make such a change, you need to inform the dialog yourself (and probably add the label to your dialog as well).
Oh, and I would recommend changing your setTitle method into
public void setTitle( String aTitle ){
super.setTitle( aTittle );
}
or remove it completely. This will avoid a StackOverflowException
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/