Model View Controller

Intent: Decouple the logic (model) from it's presentation (view), usually a GUI

In the traditional MVC it is still considered a direct interaction between the View and the Model.

A different solution permits a complete decoupling between the View and the Model.

To study this solution I realized a little Java example.

I presented this little example to my friend Riccardo Govoni, and he built a complete framework around this idea and the usage of annotations. He also published an article about this framework on Java World.

Here I present my original “simple stupid” example that doesn't make use of annotations:

MVC.java

package mvc;
 
public class MVC {
    public static void main(String[] args) {
        Model model = new Model();
        View view = new View();
 
        Controller controller = new Controller(model, view);
    }
}

Model.java

package mvc;
 
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Observer;
 
import javax.swing.Timer;
 
public class Model {
    public static final int DEFAULT_PERIOD = 60;
 
    private Timer timer;
    private int period;
 
    private ObservableInt seconds = new ObservableInt();
    private ObservableEvent askQuestion = new ObservableEvent();
 
    public Model(){
        period = DEFAULT_PERIOD;
        timer = new Timer(1000, new timerListener());
    }
 
    private class timerListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            seconds.increment();
            if (seconds.get() >= period) {
                timer.stop();
                askQuestion.notifyEvent();
                seconds.set(0);
            }
        }
    }
 
    public void questionAnswer(boolean answer){
        if (answer) timer.start(); 
    }
 
    public void setTimer() {
        if(timer.isRunning()) {
            timer.stop();
        } else {
           timer.start();
        }
    }
 
    public int getSeconds(){
        return seconds.get();
    }
 
    public void setPeriod(int period){
        this.period = period;
    }
 
    public void addSecondsObserver(Observer controller){
        seconds.addObserver(controller);
    }
 
    public void addQuestionObserver(Observer controller){
        askQuestion.addObserver(controller);
    }
}

Controller.java

package mvc;
 
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Observable;
import java.util.Observer;
 
public class Controller {
  private View view;
  private Model model;
 
  Controller(Model model, View view){
    this.view = view;
    view.addstrStpBtnListener(new StrStpBtnListener());
    view.addSetPrdBtnListener(new SetPrdBtnListener());
    view.addClosingListener(new ClosingListener());
    view.setScnFld(0);
    view.setPrdFld(Model.DEFAULT_PERIOD);
 
    this.model = model;
    model.addSecondsObserver(new SecondsObserver());
    model.addQuestionObserver(new QuestionObserver());
  }
 
  class ClosingListener extends WindowAdapter {
    public void windowClosing(WindowEvent e) {
      System.exit(0);
    }
  }
 
  class StrStpBtnListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      model.setTimer();
    }
  }
 
  class SetPrdBtnListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      int period = view.getPrdFld();
      model.setPeriod(period);
    }
  }
 
  class SecondsObserver implements Observer {
    public void update(Observable obs, Object arg) {
      view.setScnFld(model.getSeconds());
    }
  }
 
  class QuestionObserver implements Observer {
    public void update(Observable obs, Object arg) {
      model.questionAnswer(view.question());
    }
  }
}

ObservableInt.java

package mvc;
 
import java.util.Observable;
 
public class ObservableInt extends Observable {
  private int value = 0;
 
  public ObservableInt() {}
 
  public ObservableInt(int startingValue) {
    value = startingValue;
  }
 
  public int get(){
    return value;
  }
 
  public void set(int newValue){
    if(newValue != value){
      value = newValue;
      notifyChange();
    }
  }
 
  public void increment(){
    value++;
    notifyChange();
  }
 
  // Notify the observers calling the update method.
  private void notifyChange(){
    setChanged();
    notifyObservers(new Integer(value));
  }
}

ObservableEvent.java

package mvc;
 
import java.util.Observable;
 
public class ObservableEvent extends Observable {
    public void notifyEvent(){
    setChanged();
    notifyObservers();
  }
}

View.java

package mvc;
 
import java.awt.Container;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
 
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
 
public class View extends JFrame {
  private JTextField scnFld = new JTextField(5);
  private JTextField prdFld = new JTextField(5);
  private JButton setPrdBtn = new JButton("Set");
  private JButton strStpBtn = new JButton("Start/Stop");
 
  public View(){
    super("View");
 
    // Container
    Container container = this.getContentPane();
 
    // Layout
    GridBagLayout gbl = new GridBagLayout();
    container.setLayout(gbl);
    GridBagConstraints format = new GridBagConstraints();
 
    //(0,0) Seconds Label
    JLabel scnLbl = new JLabel("Seconds");
    format = new GridBagConstraints(0,0, 1,1, 0.0,0.0, GridBagConstraints.CENTER,GridBagConstraints.BOTH, new Insets(0,0,0,0), 0,0);
    gbl.setConstraints(scnLbl, format);
    container.add(scnLbl);
 
    //(1,0) Seconds Filed
    scnFld.setEditable(false);
    format = new GridBagConstraints(1,0, 1,1, 0.0,0.0, GridBagConstraints.CENTER,GridBagConstraints.BOTH, new Insets(0,0,0,0), 0,0);
    gbl.setConstraints(scnFld, format);
    container.add(scnFld);
 
    //(2,0) Start/Stop Button
    format = new GridBagConstraints(2,0, 1,1, 0.0,0.0, GridBagConstraints.CENTER,GridBagConstraints.BOTH, new Insets(0,0,0,0), 0,0);
    gbl.setConstraints(strStpBtn, format);
    container.add(strStpBtn);
 
    //(0,1) Period Label
    JLabel prdLbl = new JLabel("Period");
    format = new GridBagConstraints(0,1, 1,1, 0.0,0.0, GridBagConstraints.CENTER,GridBagConstraints.BOTH, new Insets(0,0,0,0), 0,0);
    gbl.setConstraints(prdLbl, format);
    container.add(prdLbl);
 
    //(1,1) Period Field
    format = new GridBagConstraints(1,1, 1,1, 0.0,0.0, GridBagConstraints.CENTER,GridBagConstraints.BOTH, new Insets(0,0,0,0), 0,0);
    gbl.setConstraints(prdFld, format);
    container.add(prdFld);
 
    //(2,1) Set Period Button
    format = new GridBagConstraints(2,1, 1,1, 0.0,0.0, GridBagConstraints.CENTER,GridBagConstraints.BOTH, new Insets(0,0,0,0), 0,0);
    gbl.setConstraints(setPrdBtn, format);
    container.add(setPrdBtn);
 
    // Show frame
    this.pack();
    this.setResizable(false);
    this.setLocation(250,250);
    this.setVisible(true);
  }
 
  // Closing Window Listener
  public void addClosingListener(WindowAdapter wa){
    this.addWindowListener(wa);
  }
 
  // Start/Stop Button Listener
  public void addstrStpBtnListener(ActionListener actLst) {
    strStpBtn.addActionListener(actLst);
  }
 
  // Set Period Button Listener
  public void addSetPrdBtnListener(ActionListener actLst) {
    setPrdBtn.addActionListener(actLst);
  }
 
  // Set seconds Field
  public void setScnFld(int sec){
    scnFld.setText((new Integer(sec)).toString());
  }
 
  // Set seconds Field
  public void setPrdFld(int period){
      prdFld.setText((new Integer(period)).toString());
  }
 
  // Get period Field 
  public int getPrdFld() {
    return Integer.parseInt(prdFld.getText());
  }
 
  // Question
  public boolean question(){
    int confirmation = JOptionPane.showConfirmDialog(null, "Restart ?", "Information", JOptionPane.YES_NO_OPTION);
    if (confirmation == 0) return true;
    if (confirmation == 1) return false;
 
    return false;
  }
}

java/design_patterns/model_view_controller.txt · Last modified: 2011/01/06 by emilmont
CC Attribution-Noncommercial-Share Alike 3.0 Unported
Valid CSS Driven by DokuWiki Recent changes RSS feed Valid XHTML 1.0