Introduce Model and ViewModel factories, Extract interfaces out of models

Replace some duplicated classnames with var keyword
Fix a hierarchy violation in the view class
This commit is contained in:
Marcel Schwarz 2021-11-03 23:20:53 +01:00
parent 2cfa7130fc
commit 016d6db839
13 changed files with 139 additions and 55 deletions

View File

@ -1,7 +1,8 @@
package de.icaotix.ultimatetictactoe;
import de.icaotix.ultimatetictactoe.model.ModelFactory;
import de.icaotix.ultimatetictactoe.view.UltimateTicTacToeView;
import de.icaotix.ultimatetictactoe.viewmodel.UltimateTicTacToePanelViewModel;
import de.icaotix.ultimatetictactoe.viewmodel.ViewModelFactory;
import javax.swing.*;
@ -9,7 +10,11 @@ public class Main {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
final UltimateTicTacToePanelViewModel ultimateTicTacToePanelViewModel = new UltimateTicTacToePanelViewModel();
ModelFactory modelFactory = new ModelFactory();
ViewModelFactory viewModelFactory = new ViewModelFactory(modelFactory);
final var ultimateTicTacToePanelViewModel = viewModelFactory.getUltimateTicTacToePanelViewModel();
new UltimateTicTacToeView(ultimateTicTacToePanelViewModel);
ultimateTicTacToePanelViewModel.prepareNextMove();
});

View File

@ -0,0 +1,18 @@
package de.icaotix.ultimatetictactoe.model;
import de.icaotix.ultimatetictactoe.model.definitions.CellState;
import de.icaotix.ultimatetictactoe.model.definitions.GameState;
import java.util.List;
public interface ITicTacToeGame {
void setCellState(int cell, CellState state);
CellState[] getCells();
List<Integer> getAvailableFields();
boolean isFinished();
GameState getState();
}

View File

@ -0,0 +1,16 @@
package de.icaotix.ultimatetictactoe.model;
import de.icaotix.ultimatetictactoe.model.definitions.GameState;
import de.icaotix.ultimatetictactoe.model.definitions.Player;
public interface IUltimateTicTacToe {
ITicTacToeGame getSubGame(int id);
int getActiveField();
Player getCurrentPlayer();
GameState getGameState();
void doPlayerMove(int cell);
}

View File

@ -0,0 +1,14 @@
package de.icaotix.ultimatetictactoe.model;
public class ModelFactory {
private final UltimateTicTacToe ultimateTicTacToe;
public ModelFactory() {
this.ultimateTicTacToe = new UltimateTicTacToe();
}
public IUltimateTicTacToe getUltimateTicTacToe() {
return ultimateTicTacToe;
}
}

View File

@ -1,6 +0,0 @@
package de.icaotix.ultimatetictactoe.model;
public enum Player {
X,
O
}

View File

@ -1,11 +1,14 @@
package de.icaotix.ultimatetictactoe.model;
import de.icaotix.ultimatetictactoe.model.definitions.CellState;
import de.icaotix.ultimatetictactoe.model.definitions.GameState;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
public class TicTacToeGame {
public class TicTacToeGame implements ITicTacToeGame {
public static final Integer[][] winningCombinations = new Integer[][]{
{0, 1, 2},
@ -27,6 +30,22 @@ public class TicTacToeGame {
this.state = GameState.RUNNING;
}
// DEBUG
public static void printField(ITicTacToeGame game) {
final var cells = game.getCells();
System.out.println("--------------CURRENT STATE--------------");
for (int i = 0; i < cells.length; i++) {
System.out.print(cells[i] + " ");
if ((i + 1) % 3 == 0) {
System.out.println();
}
}
System.out.println("FREE: " + game.getAvailableFields());
System.out.println("STATE: " + game.getState());
System.out.println("IS FINISHED: " + game.isFinished());
}
@Override
public void setCellState(int cell, CellState state) {
if (this.state != GameState.RUNNING) return;
if (this.cells[cell] == CellState.EMPTY) {
@ -35,10 +54,12 @@ public class TicTacToeGame {
}
}
@Override
public CellState[] getCells() {
return cells;
}
@Override
public List<Integer> getAvailableFields() {
final LinkedList<Integer> emptyFields = new LinkedList<>();
for (int i = 0; i < this.cells.length; i++) {
@ -49,17 +70,19 @@ public class TicTacToeGame {
return emptyFields;
}
@Override
public boolean isFinished() {
return this.state != GameState.RUNNING;
}
@Override
public GameState getState() {
return state;
}
private void updateGameState() {
for (Integer[] winningCombination : winningCombinations) {
final HashSet<CellState> interestingStates = new HashSet<>();
final var interestingStates = new HashSet<CellState>();
interestingStates.add(this.cells[winningCombination[0]]);
interestingStates.add(this.cells[winningCombination[1]]);
interestingStates.add(this.cells[winningCombination[2]]);
@ -77,19 +100,4 @@ public class TicTacToeGame {
}
}
// DEBUG
public static void printField(TicTacToeGame game) {
final CellState[] cells = game.getCells();
System.out.println("--------------CURRENT STATE--------------");
for (int i = 0; i < cells.length; i++) {
System.out.print(cells[i] + " ");
if ((i + 1) % 3 == 0) {
System.out.println();
}
}
System.out.println("FREE: " + game.getAvailableFields());
System.out.println("STATE: " + game.getState());
System.out.println("IS FINISHED: " + game.isFinished());
}
}

View File

@ -1,18 +1,22 @@
package de.icaotix.ultimatetictactoe.model;
import de.icaotix.ultimatetictactoe.model.definitions.CellState;
import de.icaotix.ultimatetictactoe.model.definitions.GameState;
import de.icaotix.ultimatetictactoe.model.definitions.Player;
import java.util.Arrays;
import java.util.HashSet;
public class UltimateTicTacToe {
public class UltimateTicTacToe implements IUltimateTicTacToe {
private final TicTacToeGame[] subGames;
private final ITicTacToeGame[] subGames;
private final GameState[] masterGameStates;
private Player currentPlayer;
private int activeField;
private GameState gameState;
public UltimateTicTacToe() {
this.subGames = new TicTacToeGame[9];
this.subGames = new ITicTacToeGame[9];
for (int i = 0; i < this.subGames.length; i++) {
this.subGames[i] = new TicTacToeGame();
}
@ -25,26 +29,31 @@ public class UltimateTicTacToe {
this.gameState = GameState.RUNNING;
}
public TicTacToeGame getSubGame(int id) {
@Override
public ITicTacToeGame getSubGame(int id) {
return this.subGames[id];
}
@Override
public int getActiveField() {
return activeField;
}
@Override
public Player getCurrentPlayer() {
return this.currentPlayer;
}
@Override
public GameState getGameState() {
return gameState;
}
@Override
public void doPlayerMove(int cell) {
if (!getSubGame(this.activeField).getAvailableFields().contains(cell)) return;
CellState nextCellState = this.currentPlayer == Player.X ? CellState.X : CellState.O;
var nextCellState = this.currentPlayer == Player.X ? CellState.X : CellState.O;
getSubGame(this.activeField).setCellState(cell, nextCellState);
this.masterGameStates[this.activeField] = getSubGame(this.activeField).getState();
@ -60,7 +69,7 @@ public class UltimateTicTacToe {
private void updateGameState() {
for (Integer[] winningCombination : TicTacToeGame.winningCombinations) {
final HashSet<GameState> interestingStates = new HashSet<>();
final var interestingStates = new HashSet<GameState>();
interestingStates.add(this.masterGameStates[winningCombination[0]]);
interestingStates.add(this.masterGameStates[winningCombination[1]]);
interestingStates.add(this.masterGameStates[winningCombination[2]]);

View File

@ -1,4 +1,4 @@
package de.icaotix.ultimatetictactoe.model;
package de.icaotix.ultimatetictactoe.model.definitions;
public enum CellState {
X("X"),

View File

@ -1,4 +1,4 @@
package de.icaotix.ultimatetictactoe.model;
package de.icaotix.ultimatetictactoe.model.definitions;
public enum GameState {
X_WON("X won"),

View File

@ -0,0 +1,6 @@
package de.icaotix.ultimatetictactoe.model.definitions;
public enum Player {
X,
O
}

View File

@ -1,6 +1,5 @@
package de.icaotix.ultimatetictactoe.view;
import de.icaotix.ultimatetictactoe.model.Player;
import de.icaotix.ultimatetictactoe.viewmodel.TicTacToePanelViewModel;
import de.icaotix.ultimatetictactoe.viewmodel.UltimateTicTacToePanelViewModel;
@ -53,11 +52,7 @@ public class UltimateTicTacToeView extends JFrame {
this.getContentPane().repaint();
}
public void setCurrentPlayer(Player player) {
if (player == Player.O) {
this.currentPlayerLabel.setText("Current player: O");
} else if (player == Player.X) {
this.currentPlayerLabel.setText("Current player: X");
}
public void setCurrentPlayer(String playerText) {
this.currentPlayerLabel.setText(playerText);
}
}

View File

@ -1,27 +1,25 @@
package de.icaotix.ultimatetictactoe.viewmodel;
import de.icaotix.ultimatetictactoe.model.GameState;
import de.icaotix.ultimatetictactoe.model.Player;
import de.icaotix.ultimatetictactoe.model.TicTacToeGame;
import de.icaotix.ultimatetictactoe.model.UltimateTicTacToe;
import de.icaotix.ultimatetictactoe.model.IUltimateTicTacToe;
import de.icaotix.ultimatetictactoe.model.ModelFactory;
import de.icaotix.ultimatetictactoe.model.definitions.GameState;
import java.util.List;
import java.util.function.Consumer;
public class UltimateTicTacToePanelViewModel {
private final TicTacToePanelViewModel[] subGameViewModels;
private final UltimateTicTacToe ultimateTicTacToe;
private final IUltimateTicTacToe ultimateTicTacToe;
private Consumer<String> gameResultCallback;
private Consumer<Player> currentPlayerCallback;
private Consumer<String> currentPlayerCallback;
public UltimateTicTacToePanelViewModel() {
public UltimateTicTacToePanelViewModel(ModelFactory modelFactory, ViewModelFactory viewModelFactory) {
this.subGameViewModels = new TicTacToePanelViewModel[9];
for (int i = 0; i < this.subGameViewModels.length; i++) {
this.subGameViewModels[i] = new TicTacToePanelViewModel(this);
this.subGameViewModels[i] = viewModelFactory.getTicTacToePanelViewModel(this);
}
this.ultimateTicTacToe = new UltimateTicTacToe();
this.ultimateTicTacToe = modelFactory.getUltimateTicTacToe();
}
public void prepareNextMove() {
@ -35,15 +33,16 @@ public class UltimateTicTacToePanelViewModel {
}
// Setup next move
final int fieldId = this.ultimateTicTacToe.getActiveField();
final List<Integer> availableFields = this.ultimateTicTacToe.getSubGame(fieldId).getAvailableFields();
final var availableFields = this.ultimateTicTacToe.getSubGame(fieldId).getAvailableFields();
this.subGameViewModels[fieldId].activate(availableFields);
this.currentPlayerCallback.accept(this.ultimateTicTacToe.getCurrentPlayer());
this.currentPlayerCallback.accept("Current player: "
+ this.ultimateTicTacToe.getCurrentPlayer().name());
}
public void onCellClicked(int cell) {
final int oldActiveGameId = this.ultimateTicTacToe.getActiveField();
final TicTacToePanelViewModel oldActiveGameViewModel = this.subGameViewModels[oldActiveGameId];
final TicTacToeGame oldActiveGame = this.ultimateTicTacToe.getSubGame(oldActiveGameId);
final var oldActiveGameViewModel = this.subGameViewModels[oldActiveGameId];
final var oldActiveGame = this.ultimateTicTacToe.getSubGame(oldActiveGameId);
oldActiveGameViewModel.deactivate();
this.ultimateTicTacToe.doPlayerMove(cell);
@ -67,7 +66,7 @@ public class UltimateTicTacToePanelViewModel {
this.gameResultCallback = gameResultCallback;
}
public void setCurrentPlayerCallback(Consumer<Player> currentPlayerCallback) {
public void setCurrentPlayerCallback(Consumer<String> currentPlayerCallback) {
this.currentPlayerCallback = currentPlayerCallback;
}
}

View File

@ -0,0 +1,20 @@
package de.icaotix.ultimatetictactoe.viewmodel;
import de.icaotix.ultimatetictactoe.model.ModelFactory;
public class ViewModelFactory {
private final ModelFactory modelFactory;
public ViewModelFactory(ModelFactory modelFactory) {
this.modelFactory = modelFactory;
}
public UltimateTicTacToePanelViewModel getUltimateTicTacToePanelViewModel() {
return new UltimateTicTacToePanelViewModel(this.modelFactory, this);
}
public TicTacToePanelViewModel getTicTacToePanelViewModel(UltimateTicTacToePanelViewModel ultimateTicTacToePanelViewModel) {
return new TicTacToePanelViewModel(ultimateTicTacToePanelViewModel);
}
}