Dear reader of Juri's TechBlog,
I moved my blog to a new domain and a new hosting solution as well. I'm now blogging on juristr.com.

Logical separation with MVC

I'm currently working on a personal project (which I'll publish here, so keep an eye on my blog ;) ) which I wanted to construct optimally in terms of the architecture. So today I decided to take a piece from the architectural part of my Bsc. thesis and publish it on my blog here. In specific I'll write about the MVC paradigm.

MVC is the acronym for "Model View Controller" and is a design pattern whose aim is to logically separate the application into a model-,controller- and view-part.
The Model comprises the domain model objects of the application. It contains the data and encodes the rules for giving access to this data.
The View is the end-point of the application which interacts directly with the user by presenting the data contained in the model. If the model changes the view has to be updated. This can be done by using the so-called "push" approach where the view registers itself at the model for changes or the "pull" approach where the view itself has to call the appropriate methods on the model for retrieving the updated information.
The Controller translates the user interactions on the view into actions on the model.

This figure shows a common MVC implementation:

Here the view registers itself on the model for listening to property changes. User interactions on the view are forwarded to the controller which performs some actions on the model. These changes of properties on the model side are again reflected on the view (since the view listens for changes on it). The disadvantage of this kind of MVC structure is the coupling between the view and the model.
Therefore I prefer a slightly different and more recent implementation of the MVC paradigm. The figure below outlines its basic structure.
In this example the view and the model are completely separated and work independently. The controller works as mediator between them by forwarding the according user actions from the view down to the model and the respective property change events back up to the view. This kind of structure keeps the application much more flexible and more loosely coupled. The view could be exchanged with nearly no changes since it doesn't know anything about the domain model it is representing.
Sounds nice, doesn't it?? But to not just illustrate the theoretical aspects and for a better understanding I decided to code a small demo application according to which one is able to better understand the concepts I was talking about above. Before starting to talk about it you can download it from my Open Source projects download page:

The application just manages a list of people. The available operations are to add a new person and to modify its properties. Nonsense, but it helps understanding MVC. The application consists of 3 different windows.
I decided to structure it like this, since in this way one can better see how the changes are done by the MVC and how the view is updated "magically" when the model is changed. When the user presses for instance the "Add new Person" button, the application directly adds a new Person object to the application model. The list of people of the window in the center is never touched, but since it is linked with the MVC to the application model, it automatically gets the new added person object.

But now lets take a look at the interesting part: the source code. In principle there are 3 major important classes which make up our MVC helper utility and which are subclassed by the corresponding domain objects of our application:
  • AbstractModel (abstract class)
  • AbstractController (abstract class)
  • IView (interface)
AbstractModel.java
package com.jsdevelopment.mvcdemo.mvcutils;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;

public abstract class AbstractModel{
protected PropertyChangeSupport propertyChangeSupport;

public AbstractModel(){
propertyChangeSupport = new PropertyChangeSupport(this);
}

public void addPropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(listener);
}

public void removePropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.removePropertyChangeListener(listener);
}

protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
propertyChangeSupport.firePropertyChange(propertyName, oldValue, newValue);
}

protected void fireInitialProperties(){
}
}
The AbstractModel is subclassed by our domain model classes, adding the property change support to them. The changes we have to do on our model are just the following:

package com.jsdevelopment.mvcdemo.model;

import com.jsdevelopment.mvcdemo.controller.PersonController;
import com.jsdevelopment.mvcdemo.mvcutils.AbstractModel;

public class Person extends AbstractModel {
private String firstname;
private String surname;
private int age;

...

public void setAge(Integer age) {
...
}

...

public void setFirstname(String firstname) {
String oldValue = this.firstname;
this.firstname = firstname;
firePropertyChange(PersonController.FIRSTNAME_PROPERTY, oldValue, firstname);
}

..

public void setSurname(String surname) {
...
}

public void fireInitialProperties(){
firePropertyChange(PersonController.AGE_PROPERTY, null, age);
firePropertyChange(PersonController.FIRSTNAME_PROPERTY, null, firstname);
firePropertyChange(PersonController.SURNAME_PROPERTY, null, surname);
}
...
}
I've hidden the getters, since the interesting part here are the setters since the action happens there. Take for instance the setFirstname(String) method. What I do there is to keep the old value, then I set the new value, just as usual and then we fire the property-change event which we inherited from the AbstractModel class.
Lets move over to our AbstractController. Here's the abbreviated source code:

AbstractController.java
package com.jsdevelopment.mvcdemo.mvcutils;
...
public abstract class AbstractController implements PropertyChangeListener {

private ArrayList<IView> registeredViews;
private ArrayList<AbstractModel> registeredModels;

public AbstractController() {
registeredViews = new ArrayList<IView>();
registeredModels = new ArrayList<AbstractModel>();
}

...

public void propertyChange(PropertyChangeEvent evt) {

for (IView view: registeredViews) {
view.modelPropertyChange(evt);
}
}

protected void setModelProperty(String propertyName, Object newValue) {

for (AbstractModel model: registeredModels) {
try {

Method method = model.getClass().
getMethod("set"+propertyName, new Class[] {
newValue.getClass()
}


);
method.invoke(model, newValue);

} catch (Exception ex) {
// Handle exception.
}
}
}
}
The AbstractController class is mainly responsible for propagating the corresponding events either from the model to the view or vice-versa. The following two methods are especially interesting since they are somehow the core of the MVC:
  • propertyChange(PropertyChangeEvent)
    This method is responsible for propagating a change of a property from the model to the view. As you can see it iterates over the list of registered views and calls the method modelPropertyChange(...) which is implemented by our views.
  • setModelProperty(String, Object)
    This method - as you probably guess already - is responsible for propagating the user-actions on the view down to the model. This method is especially tricky since it uses the Java reflection mechanism for finding the right method to invoke. As you can see from the code above it calls the setter on the registered models which corresponds to the given property-event.
Our specific controller - in this demo application the PersonController - has not much to do actually:
package com.jsdevelopment.mvcdemo.controller;

import com.jsdevelopment.mvcdemo.mvcutils.AbstractController;

/**
* Controller for synchronizing the Person domain object
* with the respective View
*
*/
public class PersonController extends AbstractController {
public static final String FIRSTNAME_PROPERTY = "Firstname";
public static final String SURNAME_PROPERTY = "Surname";
public static final String AGE_PROPERTY = "Age";

public PersonController(){
}

public void changeFirstName(String newValue){
setModelProperty(FIRSTNAME_PROPERTY, newValue);
}

public void changeSurname(String newValue){
setModelProperty(SURNAME_PROPERTY, newValue);
}

public void changeAge(String newValue){
Integer value = Integer.parseInt(newValue);
setModelProperty(AGE_PROPERTY, value);
}
}
So, lets see what's interesting here. For the first you can see that it inherits from our AbstractController class we've seen before. The next thing which catch one's eye are the static constants defined at the beginning. If you take a look at the AbstractModel you'll see that these constants are used for identifying the type of property change. Moreover in the AbstractController.setModelProperty(...) method it is used for constructing the right signature of the setter-method of the domain model which has to be invoked. And then there are the different methods such as changeFirstName and so on. These methods are directly called by the view (we'll see that below) and they then call the setModelProperty(...) method for propagating the change down to the model.
Finally we can move on to the 3rd component: the view.

IView.java
package com.jsdevelopment.mvcdemo.mvcutils;

import java.beans.PropertyChangeEvent;

public interface IView {
public void modelPropertyChange(PropertyChangeEvent evt);
}
This class is really simple. It is implemented as an interface for grouping all of our views together and for constraining them to implement the modelPropertyChange(...) method which is called by the AbstractController. The essential parts on our view are then the following:
package com.jsdevelopment.mvcdemo.view;
...

import com.jsdevelopment.mvcdemo.controller.PersonController;
import com.jsdevelopment.mvcdemo.mvcutils.AbstractController;
import com.jsdevelopment.mvcdemo.mvcutils.IView;

public class PersonView extends JFrame implements IView {
...
private PersonController controller = null;
...

/**
* This method initializes
*
*/
public PersonView(AbstractController controller) {
super();
initialize();
this.controller = (PersonController)controller;
}

...

private JButton getJButtonSave() {
if (jButtonSave == null) {
jButtonSave = new JButton();
jButtonSave.setBounds(new Rectangle(112, 102, 70, 24));
jButtonSave.setText("Save");
jButtonSave.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent e) {
//fire all property changes
controller.changeFirstName(jTextFieldFirstname.getText());
controller.changeSurname(jTextFieldSurname.getText());
controller.changeAge(jTextFieldAge.getText());

closeFrame();
}
});
}
return jButtonSave;
}

public void modelPropertyChange(PropertyChangeEvent evt) {
if(evt.getNewValue() == null) return;

String value = evt.getNewValue().toString();

if(evt.getPropertyName().equals(PersonController.FIRSTNAME_PROPERTY)){
jTextFieldFirstname.setText(value);
}else if(evt.getPropertyName().equals(PersonController.SURNAME_PROPERTY)){
jTextFieldSurname.setText(value);
}else if(evt.getPropertyName().equals(PersonController.AGE_PROPERTY)){
jTextFieldAge.setText(value);
}
}
}
The first thing here is to get a reference to our actual controller class. In my example here this is done directly in the constructor. Once could however also do this with a separate setter. If you go further, you'll come to the listener attached to the save button. I've decided to propagate all actions down to the model when the save-button is clicked. So what I do there is just to call the methods of my controller which we have seen before. That's all. Finally we have our modelPropertyChange(...) method which we inherited from our IView interface (remember?) and which is implemented here. This method gets invoked when our AbstractController gets an property-change event notification from one of our domain objects. Here we just ask which property has been fired and then we set the value on the appropriate view control.

So since we've covered now the critical part lets move over to the actual binding of these components. In my demo application this is done in the ApplicationController:
package com.jsdevelopment.mvcdemo.controller;

import com.jsdevelopment.mvcdemo.model.Person;
import com.jsdevelopment.mvcdemo.mvcutils.AbstractController;
import com.jsdevelopment.mvcdemo.view.PersonView;


public class ApplicationController extends AbstractController {
...

public void showPersonView(Object selectedValue) {
//construct the MVC binding
Person p = (Person)selectedValue;
pController = new PersonController();
PersonView pView = new PersonView(pController);
pController.addModel(p);
pController.addView(pView);


//show the view
pView.setVisible(true);

//display initial properties on view by firing the property actions
p.fireInitialProperties();
}
}
Here inside the showPersonView(...) method I simply create a new PersonController object and I associate the given domain object of type Person and add it to the controller as well as the reference of my view which will present the content of the object to the end-user. This piece of code is just to demonstrate the binding and may not be that optimal. So it is not really advisable to create a new controller object each time but you'd have to maybe create it the first time and then to register and unregister the different models and views. What may still make you wonder is the last line where I invoke the fireInitialProperties() method. This is done to fill the view with the initial values of the domain object (if it contains some).

Well, that was it. Easy isn't it ;). The best thing is again to download the demo application and study its source code which is included in the archive. Otherwise you can also access it on the public SVN repository. Feel free to put your comment if you have further question or suggestions for better solutions.

Here's a post on the Google Testing blog that highlights the advantages of such an MVC structure:
http://googletesting.blogspot.com/2009/02/with-all-sport-drug-scandals-of-late.html

Posts you might also be interested in..

Credits: Hoctro | Jack Book

0 Comments:

Post a Comment