Android by example : MVVM +Data Binding -> Model (Part 2)

Husayn Hakeem

This article is part 2/4 from a series or articles about implementing the MVVM design pattern and data binding in a sample Tic-Tac-Toe application. If you’re unfamiliar with the terms data binding or MVVM pattern, refer to part 1 for a quick refresher before continuing along this part.

Note: You’ll find all the code for this project in the following Github repo.

M for Model

The model in this application is composed of 3 classes:

  • Player
  • Cell
  • Game

Player

Has 2 attributes, a name and a value.

public class Player {

public String name;
public String value;

public Player(String name, String value) {
this.name = name;
this.value = value;
}
}

Cell

Has a single attribute, a player instance.

public class Cell {

public Player player;

public Cell(Player player) {
this.player = player;
}

public boolean isEmpty() {
return player == null || StringUtility.isNullOrEmpty(player.value);
}
}

Game

Represents an actual Tic-Tac-Toe game, and thus has 2 players, a list (3x3 matrix) of 9 cells. In each round of the game one player makes a move, hence the attribute currentPlayer, and finally a winner.

ps: If you’re not familiar with MutableLiveData, don’t freak out! Just ignore it, consider the winner attribute to be of type Player.

public class Game {

private static final String TAG = Game.class.getSimpleName();
private static final int BOARD_SIZE = 3;

public Player player1;
public Player player2;

public Player currentPlayer = player1;
public Cell[][] cells;

public MutableLiveData<Player> winner = new MutableLiveData<>();
public Game(String playerOne, String playerTwo) {
cells = new Cell[BOARD_SIZE][BOARD_SIZE];
player1 = new Player(playerOne, "x");
player2 = new Player(playerTwo, "o");
currentPlayer = player1;
}

public void switchPlayer() {
currentPlayer = currentPlayer == player1 ? player2 : player1;
}
}

We should be able to know when a game ends, which is why after each round (i.e move made a either of the players), we need to check if the game has ended. The game ends if the board (cells) have 3 identical horizontal, vertical or diagonal cells, or if there are no more empty cells.

public boolean hasGameEnded() {
if (hasThreeSameHorizontalCells() || hasThreeSameVerticalCells() || hasThreeSameDiagonalCells()) {
winner.setValue(currentPlayer);
return true;
}

if (isBoardFull()) {
winner.setValue(null);
return true;
}

return false;
}

public boolean hasThreeSameHorizontalCells() {
try {
for (int i = 0; i < BOARD_SIZE; i++)
if (areEqual(cells[i][0], cells[i][1], cells[i][2]))
return true;

return false;
} catch (NullPointerException e) {
Log.e(TAG, e.getMessage());
return false;
}
}

public boolean hasThreeSameVerticalCells() {
try {
for (int i = 0; i < BOARD_SIZE; i++)
if (areEqual(cells[0][i], cells[1][i], cells[2][i]))
return true;
return false;
} catch (NullPointerException e) {
Log.e(TAG, e.getMessage());
return false;
}
}

public boolean hasThreeSameDiagonalCells() {
try {
return areEqual(cells[0][0], cells[1][1], cells[2][2]) ||
areEqual(cells[0][2], cells[1][1], cells[2][0]);
} catch (NullPointerException e) {
Log.e(TAG, e.getMessage());
return false;
}
}


public boolean isBoardFull() {
for (Cell[] row : cells)
for (Cell cell : row)
if (cell == null ||cell.isEmpty())
return false;
return true;
}
/**
* 2 cells are equal if:
* - Both are none null
* - Both have non null values
* - both have equal values
*
*
@param cells: Cells to check if are equal
*
@return
*/
private boolean areEqual(Cell... cells) {
if (cells == null || cells.length == 0)
return false;

for (Cell cell : cells)
if (cell == null || cell.player.value == null || cell.player.value.length() == 0)
return false;

Cell comparisonBase = cells[0];
for (int i = 1; i < cells.length; i++)
if (!comparisonBase.player.value.equals(cells[i].player.value))
return false;

return true;
}

Finally, once a game ends, all attributes related to this game instance should be re-initialized.

public void reset() {
player1 = null;
player2 = null;
currentPlayer = null;
cells = null;
}

As you can see, the models are modular classes representing the data, state and business logic of our Tic-Tac-Toe application. They describe the elements which are the base of our whole app (players, cells), they check whether the game is still ongoing or has ended and know what actions to take when a game is finished, all of the logic is written, now all we need is a component that will orchestrate the whole game.. That’s the View Model, which we’ll build in part 3.

ps: The fact that our Model classes are modular make testing them very easy. They don’t interact with the UI, which means Junit tests are enough for testing whether they function correctly.


For more on Java, Kotlin and Android, follow me to get notified when I write new posts, or let’s connect on Github, Twitter and LinkedIn!

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade