Chapter 8.J

Java MVC

MVC in Java

Subsections of Java MVC

A Worked Example - Model

Let’s continue our example exploring a program to play Connect Four by actually building the code to implement the game. By exploring the code, we should be able to better understand the structure and reasoning behind the MVC Architecture.

Problem Statement

First, let’s formalize our problem statement:

Write a program to play the traditional version of Connect Four. It should take place on a 6 x 7 game board.

On each turn, the game will print out the state of the board, then alert the current player to make a move. The player’s move will consist of a single number, giving the column in which to place a piece. If the move is invalid, the player will be given the option to select another move.

After each move is made, the game will determine if the current player has won, or if the game was a draw. To simplify the game, we will not check for diagonal wins.

The program should be built using MVC architecture.

So, for this program, we’ll use the following UML diagram:

Connect Four UML Diagram Connect Four UML Diagram

Let’s get started!

Model

YouTube Video

Video Materials

In many cases, the first part of any program following the MVC architecture to build is the model. This is usually the simplest and most straightforward part of the program, and both the view and the controller really depend on the model as the basis of the entire program. So, let’s start there.

First, we’ll need to build a class that contains the attributes and methods shown in the UML diagram above. By this point, we should be pretty used to this process:

import java.lang.IllegalArgumentException;

public class ConnectModel{

  private int[][] board;
  private int current_player;
  
  public int getCurrentPlayer(){ return this.current_player; }
  public int[][] getBoard(){ return this.board; }

  // more code here
}

This code already contains implementations for the two getter methods as well as the declarations for the two private attributes. So, all we have left to do is implement the constructors and a few methods.

Let’s focus on the constructors first. We can build the normal constructor as shown below:

public ConnectModel(int m, int n){
  if(m < 4 || n < 5){
    throw new IllegalArgumentException("The board must be at least 4 x 5");
  }
  this.board = new int[m][n];
  this.current_player = 1;
}

This method really consists of two parts. First, we verify that the parameters provided are valid. If not, we’ll throw an exception. Then, it initializes the board attribute to the correct dimensions. We also set the current player to 1.

We also need to implement our constructor for testing, which simply accepts a board and a current player:

public ConnectModel(int[][] board, int current_player){
  if(board.length < 4){
    throw new IllegalArgumentException("Board must be at least 4 x 5");
  }
  if(board[0].length < 5){
    throw new IllegalArgumentException("Board must be at least 4 x 5");
  }
  if(current_player != 1 && current_player != 2){
    throw new IllegalArgumentException("Current player invalid - must be 1 or 2");
  }
  this.board = board;
  this.current_player = current_player;
}

This constructor simply enforces the size of the board and the values that are possible for the current player, and then sets those values to the ones provided as arguments. As we discussed before, this method is primarily used for testing our model, and won’t actually be used in our program itself.

Once we have our constructors built, we can also build our makeMove() method:

public boolean makeMove(int y){
  if(y < 0 || y >= this.board[0].length){
    throw new IllegalArgumentException("Invalid column");
  }
  if(this.board[0][y] != 0){
    throw new IllegalArgumentException("Column full");
  }
  int row = this.board.length - 1;
  while(board[row][y] != 0){
    row = row - 1;
  }
  this.board[row][y] = this.current_player;
  return true;
}

This method is also pretty straightforward. First, we use a couple of If-Then statements to check and see if the input is valid. If it is, then we simply iterate through the column chosen from the last row up to the first, looking for the first empty slot. Once we’ve found it, we place the current player’s piece there, and return true.

There we go! That’s a simple method for checking to see if a move in Connect Four is valid according to the rules of the game.

Next, we’ll need to write a method to determine if the current player has won. In this method, we’ll simply check to see if any row or column has at least 4 consecutive pieces for the current player.

public boolean checkWin(){
  // check rows
  for(int i = 0; i < this.board.length; i++){
    int count = 0;
    for(int j = 0; j < this.board[0].length; j++){
      if(this.board[i][j] == this.current_player){
        count += 1;
      }else{
        count = 0;
      }
      if(count >= 4){
        return true;
      }
    }
  }
  
  // check columns
  for(int j = 0; j < this.board[0].length; j++){
    int count = 0;
    for(int i = 0; i < this.board.length; i++){
      if(this.board[i][j] == this.current_player){
        count += 1;
      }else{
        count = 0;
      }
      if(count >= 4){
        return true;
      }
    }
  }
  return false;
}

This method is also pretty straightforward. We simply use two nested For loops to iterate across each row and down each column, keeping track of the count of items that match the current player. Anytime we see an item that doesn’t match, we reset our count to 0. If we reach 4 at any time, we can simply return true. If we reach the end of the method without returning true, then we know that a win condition hasn’t been reached and we should return false.

We’ll also need a method to check for a draw:

public boolean checkDraw(){
  for(int j = 0; j < this.board[0].length; j++){
    if(this.board[0][j] == 0){
      return false;
    }
  }
  return true;
}

In this method, we only have to check the top row of the board. If any of them are empty, we can return false. However, if we find that all of them are filled, we can return true, indicating that the board is filled.

Finally, we’ll build one more method to switch players between 1 and 2:

public void nextPlayer(){
  if(this.current_player == 1){
    this.current_player = 2;
  }else{
    this.current_player = 1;
  }
}

It is really just a simple If-Then statement!

There we go! That should be everything we need to build a model of a working Connect Four game. We can use the assessments below to confirm that our code is working properly before continuing.

Subsections of A Worked Example - Model

A Worked Example - View

The next part of any MVC program to build is the view. Depending on the complexity of the problem, sometimes it may be helpful to build the view and the model concurrently. Ideally, however, the model and the view should be properly distinct and separate, such that the model can be fully completed before the view is developed.

Let’s review our UML diagram before continuing:

Connect Four UML Diagram Connect Four UML Diagram

View

YouTube Video

The ConnectView class only contains a few simple methods. First, let’s build the class declaration and the constructor:

import java.util.Scanner;
import java.lang.NumberFormatException;

public class ConnectView{
  
  public ConnectView(){
    //default constructor
  }
}

Since we aren’t initializing a GUI, we’ll just need a default constructor for this class.

Next, let’s look at the methods. First, we’ll need a method to show the board on the screen, called showBoard(). Let’s see what it looks like:

public void showBoard(int[][] board){
  for(int i = 0; i < board.length; i++){
    for(int j = 0; j < board[0].length; j++){
      System.out.print("[" + board[i][j] + "]");
    }
    System.out.println();
  }
}

This method is very similar to methods we saw in an earlier module when we learned about arrays. Here, we are simply using two nested For loops to print the data in the array, with each row printed on its own line.

When we run this method, we should get output that looks like this:

[1][2][1][2][1]
[1][2][1][2][1]
[1][2][1][2][1]
[1][2][1][2][1]
[1][2][1][2][1]

The other complex method to implement is the showMenu() method, which reads input from the player:

public int showMenu(){
  while(true){
    try{
      Scanner reader = new Scanner(System.in);
      System.out.println("Which column would you like to place a token in?");
      String input = reader.nextLine().trim();
      int out = Integer.parseInt(input);
      return out;
    }catch(NumberFormatException e){
      System.out.println("Invalid input! Please enter a number");
    }catch(Exception e){
      System.out.println("Unable to read input!");
      return -1;
    }
  }
}

That method will print a prompt to the user, and get a response as a string. Notice that we aren’t doing any validation of the input here beyond making sure that it is an integer—we can do that in the controller and the model.

The final three methods simply print out messages to the user. So, we can implement those pretty easily:

public void showTurn(int player){
  System.out.println("It is player " + player + "'s turn!");
}

public void showEndGame(int winner){
  if(winner == 0){
    System.out.println("The game is a draw!");
  }else{
    System.out.println("Player " + winner + " wins!");
  }
}

public void showError(String error){
  System.out.println("Error: " + error);
}

That’s it! The view portion of the program is very simple, but it allows our users to see what is going on and interact with the program. We can use the tests below to make sure our view is working correctly before we continue with the controller.

A Worked Example - Controller

Finally, we now can start working on our controller. This is where the program actually comes together and becomes useful. While a program such as Connect Four may seem quite complex, using MVC architecture can make it quite easy to work with. Let’s check it out!

Controller

YouTube Video

The ConnectController class just contains a single main() method:

public class ConnectController{
  
  public static void main(String[] args){
    ConnectModel model = new ConnectModel(6, 7);
    ConnectView view = new ConnectView();
    while(true){
      view.showBoard(model.getBoard());
      view.showTurn(model.getCurrentPlayer());
      try{
        if(model.makeMove(view.showMenu())){
          if(model.checkWin() || model.checkDraw()){
            break;
          }
          model.nextPlayer();
        }
      }catch(Exception e){
        view.showError(e.getMessage());
      }
    }
    if(model.checkWin()){
      view.showEndGame(model.getCurrentPlayer());
    }else{
      view.showEndGame(0);
    }
  }
  
}

This method uses methods in the view and model classes to get the game to work. For example, if we start inside of the while loop, we see the following steps:

  1. The game prints the current board to the screen.
  2. The game prints a message for the current user.
  3. The game then asks the user for input, and uses that input to make a move.
    1. If the move is successful, it checks to see if the game is over. If so, it exits the loop.
    2. If the move is unsuccessful, an exception is thrown and then caught and handled. The program repeats so the user has another opportunity to move.
  4. Finally, once the program exits the loop, it will print a message giving the outcome of the game.

That’s all there is to it! Once again, our main() method ends up being an outline of our program. In this case, almost every line of code is calling a method in both the model and the view, combining them in such a way as to make the program work seamlessly.

Try it out.