Saturday, March 13, 2010

Model-View-Controller (MVC) pattern

The MVC architectural pattern is the bedrock of technologies like JavaServer Faces and is fundamental to implementing three-tier architecture. It clearly segregates the model in the business layer, view in the presentation layer and the controller, which handles all the inputs affecting the model. Obviously if the model is totally segregated then it becomes relatively easier to test the domain logic with automated tools. We can illustrate this decoupling of model and view through a simple calculator application which takes two numbers and applies the requested operator to provide the result.


package mvc;

import java.awt.event.ActionEvent;
import javax.swing.*;

public class MvcTest {

private static void buildUI() {
MvcModel model = new MvcModel();
MvcView view = new MvcView();
MvcControl control = new MvcControl("Result", view, model);
view.setControl(control);

JFrame frame = new JFrame("Test Calculator");
frame.getContentPane().add(view.getMainPanel());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}

public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {

public void run() {
buildUI();
}
});
}
}

class MvcView {
// mainPanel holds all the GUI components

private JPanel mainPanel = new JPanel();
// two input fields and the result textfield
private JTextField firstOperand = new JTextField(10);
private JTextField operator = new JTextField(1);
private JTextField secondOperand = new JTextField(10);
private JTextField resultField = new JTextField(10);
private JButton addButton = new JButton();

public MvcView() {
resultField.setEditable(false);
operator.setText("+"); // default to add
mainPanel.add(firstOperand);
mainPanel.add(operator);
mainPanel.add(secondOperand);
mainPanel.add(new JLabel("="));
mainPanel.add(resultField);
mainPanel.add(addButton);
}

public void setControl(MvcControl control) {
addButton.setAction(control);
}

public JPanel getMainPanel() {
return mainPanel;
}

public String getFirstOperandText() {
return firstOperand.getText();
}

public String getSecondOperandText() {
return secondOperand.getText();
}

public String getOperatorText() {
return operator.getText();
}

public void setResultText(String text) {
resultField.setText(text);
}
}

class MvcControl extends AbstractAction {

private MvcModel model;
private MvcView view;

public MvcControl(String text, MvcView view, MvcModel model) {
super(text);
this.view = view;
this.model = model;
}

public void actionPerformed(ActionEvent e) {
view.setResultText(""); // clear the result
try {
double op1 = Double.parseDouble(view.getFirstOperandText());
double op2 = Double.parseDouble(view.getSecondOperandText());
char operator = view.getOperatorText().charAt(0);
if (operator == '+'
|| operator == '-'
|| operator == '*'
|| operator == 'x'
|| operator == 'X'
|| operator == '/'
|| operator == '%') {}
else throw new IllegalArgumentException();
double result = model.doOperation(op1, op2, operator); // use the model here
view.setResultText(String.valueOf(result));
} catch (NumberFormatException nfe) {
JOptionPane.showMessageDialog(view.getMainPanel(), "Only Numbers Allowed",
"Data Error", JOptionPane.ERROR_MESSAGE);
} catch (IllegalArgumentException nfe) {
JOptionPane.showMessageDialog(view.getMainPanel(), "The operator must be one of *, /, +, - or %",
"Data Error", JOptionPane.ERROR_MESSAGE);
}
}
}

class MvcModel {

public double doOperation(double op1, double op2, char operator) {
double result = 0;
switch (operator) {
case '+':
result = op1 + op2;
break;
case '-':
result = op1 - op2;
break;
case '*':
case 'x':
case 'X':
result = op1 * op2;
break;
case '/':
result = op1 / op2;
break;
case '%':
result = op1 % op2;
}
return result;
}
}

It is clear in our example that the view (MvcView) is the user interface which asks for two operands and the mathematical operation to apply on them. Model (MvcModel) is passed these inputs by the controller (MvcController) to get the calculation result. In our design the view has no knowledge of the model and the controller is an observer of the action performed on the 'result' button in the view. The controller uses its knowledge of the view to update the result. Thus a clear segregation of roles has been achieved. Obviously an application can have a number of these MVC triplets.






No comments:

Post a Comment