Practical MVC

Abstract

The Model-View-Controller (MVC) is one of the commonly used software design patterns [1]. Its paradigm is a way to decouple the application data, behaviour and representation, making its components easier to be reused. This article shows the practical side of MVC through an example: JMinesweeper, a Java minesweeper game. (June 2005).

Back when I was in school and learning Object Oriented Design (OOD) and Programming (OOP), I was wondering how I could write the Windows game minesweeper. Having freshly absorbed OOD theories, I pictured that each square could be an object that knows how to draw itself and how to handle actions from the user. A square object would collaborate with its neighboring squares to propagate game events such as open-a-square, etc.

Though designing minesweeper that way is sound from the OOP point of view, it is however not efficient (too many objects to create and messages between objects) and mostly difficult to change should one need a different user interface. This article shows a better way to design the minesweeper game using MVC. It emphasizes more on the design aspect rather than on the implementation aspect.

MVC Pattern

The MVC has its root in Smalltalk where it was used to map the traditional (input -> processing -> output) realm to the (controller -> model -> view) realm. The MVC is now adapted to most user graphical interface (GUI) applications, including web applications. (For Java web application, MVC is also known as the JSP Model 2 [2]). In any cases, the goal is always to decouple different aspects of the application:

See Design Patterns [1] and Sun's Java BluePrint [3] for more information on MVC.

JMinesweeper Requirements

Just like in construction where a blueprint is needed to build a house, a software needs a set of requirements that specifies what one is to develop. For JMinesweeper, my goals are simple:

The first point hides a number of rules that are enumerated in the 'Game Rules' section of the game page. The third point has to do with running the game as an applet, where loading duration is in relation with the number of classes.

Model

A minesweeper game is characterized by a certain number of properties such as the number of mines, the number of rows and columns of the minefield grid, the location of the mines in the grid, etc. Those are the application data. Along with the business rules governing their access, they form the model in in the MVC realm. In my implementation, the model is represented by Minefield.java, which defines the following properties and business rules:

private int[][] _map; The internal map of the minefield grid; first dimension being the rows, second dimension being the columns. The value of each element indicates either the presence of a mine (M_BOMB), or the number of mines adjacent to the corresponding square ([0..8]).
private int[][] _grid; The minefield grid as seen by the public. The value of each element indicates either one of the M_XXX constants or that of the corresponding _map cell once the square is open.
private int _rows, _cols; The number of rows and columns of the minefield grid.
private int _bombs, _flags; The total number of mines (bombs) in the grid and the number of squares that has been flagged.
private int _remainCells; The number of squares not yet open. This number is decremented when a square is open. A zero value means that the game is won by the user.

Beside the above properties, Minefield contains a number of public methods representing the business rules governing the access and update of the model.

public void reset(); Resets the minefield. It randomly places the mines on the grid and compute the number of adjacent mines for the other square.
public boolean isDone(); Determines whether all squares have been open.
public int flag(int r, int c); Flags or unflag the specified square. The new state if the square as a result of this action is returned. Possible returned values are M_FLAG, M_DOUBT and M_COVER
public List open(int r, int c); Opens the specified square. A list of open squares is returned. An empty list means no square has been open by the action and a null value means a mine has been open.
public List openRemaining(int r, int c); Opens the remaining squares around the specified numbered square. This action takes place if and only if the specified square (a) is already open, (b) is adjacent to one or more mine, (c) has all of its mine(s) flagged. It returns a list of open cells like open.
public List makeFinalSolution (boolean showFlag); Reveals the mines location in the external grid as final solution. If showFlag is true, the mines are shown as flags (used when the game is won).

View

In short, the view consists in displaying the model. It take me a long time to build the GUI, specially that I haven't done Swing programming for a long time. So one way do early testing is to have a text interface. That's right, minesweeper in text mode! The view in text mode is very simple: it consists in displaying the minefield's external grid in human readable format with something like the below method:

public static String toString(int[][] grid)
{
  StringBuffer sb = new StringBuffer();
  for (int r=0; r<grid.length; r++) {
    for (int c=0; c<grid[r].length; c++) {
      int val = grid[r][c];
      sb.append('[');
      switch (val) {
        case Minefield.M_COVER: sb.append(' '); break;
        case Minefield.M_BOMB:  sb.append('*'); break;
        case Minefield.M_FLAG:  sb.append('o'); break;
        case Minefield.M_DOUBT: sb.append('?'); break;
        case Minefield.M_NONE:  sb.append('.'); break;
        case Minefield.M_BAD:   sb.append('x'); break;
        default:
          sb.append(val);
      }
      sb.append(']');
    }
    sb.append('\n');
  }
  return sb.toString();
}

With no change in the model, a GUI view can also be implemented. See JMinesweeper.java for the full implementation in Java Swing. As you can see, the GUI view is just a fancier version of the text mode view. (NOTE that my implementation uses an array of JLabel objects. It is not the most efficient implementation in term of speed, but I just needed to learn Swing anew).

Controller

For the text interface, the controller is in charged of getting input from the user and translate it into actions to be performed by the model. In my text mode minesweeper, its implementation looks something like the below:

public static void main(String[] args) throws Exception
{
  Minefield mfield = new Minefield(9, 9, 10);
  boolean isOk = true;
  do {
    System.out.println(toString(mfield.getMap()));
    System.out.println(toString(mfield.getGrid()));

    System.out.print("action (1=open|2=mark|3=remain|.=quit): ");
    String action = IOUtil.readLine(System.in);
    if (".".equals(action))
      System.exit(0);

    System.out.print("row: ");
    int r = Integer.parseInt(IOUtil.readLine(System.in)) - 1;

    System.out.print("col: ");
    int c = Integer.parseInt(IOUtil.readLine(System.in)) - 1;

    if ("1".equals(action))
      isOk = (mfield.open(r, c) != null);
    else if ("2".equals(action))
      mfield.flag(r, c);
    else if ("3".equals(action))
      isOk = (mfield.openRemaining(r, c) != null);

    System.out.println("remain=" + mfield.getRemainingCells());
    System.out.println("isDone=" + mfield.isDone());
  }
  while (isOk && !mfield.isDone());

  mfield.makeFinalSolution(isOk);
  System.out.println(toString(mfield.getGrid()));
}

As for the GUI mode, the controller translates interactions with the view into actions to be performed by the model. The interactions with the view is actually already handled by Swing's events propagation model. Therefore the only thing to do is mapping the corresponding events generated by Swing to the actions to be performed by the model. It is done by implementing some pertinent interfaces such as MouseListener and ActionListener as seen in JMinesweeper.java.

Conclusion

As we can see, using MVC design pattern to decouple the application's data and business logic from the presentation layer makes components more reusable. From the minesweeper example above, one could reuse the model and make a web interface to it. It might not be practical as each interaction requires data to be sent back and forth between the browser and the server, but it is possible!

References

[1] Design Patterns
"Design Patterns: Elements of Reusable Object-Oriented Software", Erich Gamma, Richard Helm, Ralph Johnson & John Vlissides, 1995
[2] JSP Model 2
"Understanding Java Server Pages Model 2 Architecture", Govind Seshadri
[3] Java BluePrint MVC
"Java BluePrint, Model-View-Controller"
Last updated: 2005-06-01 23:04:29 -0700