From 016d6db839ea49a914a336990cef2954ab548e5f Mon Sep 17 00:00:00 2001 From: Marcel Schwarz Date: Wed, 3 Nov 2021 23:20:53 +0100 Subject: [PATCH] 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 --- .../de/icaotix/ultimatetictactoe/Main.java | 9 +++- .../model/ITicTacToeGame.java | 18 ++++++++ .../model/IUltimateTicTacToe.java | 16 +++++++ .../ultimatetictactoe/model/ModelFactory.java | 14 +++++++ .../ultimatetictactoe/model/Player.java | 6 --- .../model/TicTacToeGame.java | 42 +++++++++++-------- .../model/UltimateTicTacToe.java | 21 +++++++--- .../model/{ => definitions}/CellState.java | 2 +- .../model/{ => definitions}/GameState.java | 2 +- .../model/definitions/Player.java | 6 +++ .../view/UltimateTicTacToeView.java | 9 +--- .../UltimateTicTacToePanelViewModel.java | 29 +++++++------ .../viewmodel/ViewModelFactory.java | 20 +++++++++ 13 files changed, 139 insertions(+), 55 deletions(-) create mode 100644 src/main/java/de/icaotix/ultimatetictactoe/model/ITicTacToeGame.java create mode 100644 src/main/java/de/icaotix/ultimatetictactoe/model/IUltimateTicTacToe.java create mode 100644 src/main/java/de/icaotix/ultimatetictactoe/model/ModelFactory.java delete mode 100644 src/main/java/de/icaotix/ultimatetictactoe/model/Player.java rename src/main/java/de/icaotix/ultimatetictactoe/model/{ => definitions}/CellState.java (76%) rename src/main/java/de/icaotix/ultimatetictactoe/model/{ => definitions}/GameState.java (80%) create mode 100644 src/main/java/de/icaotix/ultimatetictactoe/model/definitions/Player.java create mode 100644 src/main/java/de/icaotix/ultimatetictactoe/viewmodel/ViewModelFactory.java diff --git a/src/main/java/de/icaotix/ultimatetictactoe/Main.java b/src/main/java/de/icaotix/ultimatetictactoe/Main.java index d04fb36..3f913bd 100644 --- a/src/main/java/de/icaotix/ultimatetictactoe/Main.java +++ b/src/main/java/de/icaotix/ultimatetictactoe/Main.java @@ -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(); }); diff --git a/src/main/java/de/icaotix/ultimatetictactoe/model/ITicTacToeGame.java b/src/main/java/de/icaotix/ultimatetictactoe/model/ITicTacToeGame.java new file mode 100644 index 0000000..088e6d2 --- /dev/null +++ b/src/main/java/de/icaotix/ultimatetictactoe/model/ITicTacToeGame.java @@ -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 getAvailableFields(); + + boolean isFinished(); + + GameState getState(); +} diff --git a/src/main/java/de/icaotix/ultimatetictactoe/model/IUltimateTicTacToe.java b/src/main/java/de/icaotix/ultimatetictactoe/model/IUltimateTicTacToe.java new file mode 100644 index 0000000..6f0e001 --- /dev/null +++ b/src/main/java/de/icaotix/ultimatetictactoe/model/IUltimateTicTacToe.java @@ -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); +} diff --git a/src/main/java/de/icaotix/ultimatetictactoe/model/ModelFactory.java b/src/main/java/de/icaotix/ultimatetictactoe/model/ModelFactory.java new file mode 100644 index 0000000..1f5f05c --- /dev/null +++ b/src/main/java/de/icaotix/ultimatetictactoe/model/ModelFactory.java @@ -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; + } +} diff --git a/src/main/java/de/icaotix/ultimatetictactoe/model/Player.java b/src/main/java/de/icaotix/ultimatetictactoe/model/Player.java deleted file mode 100644 index b6065f5..0000000 --- a/src/main/java/de/icaotix/ultimatetictactoe/model/Player.java +++ /dev/null @@ -1,6 +0,0 @@ -package de.icaotix.ultimatetictactoe.model; - -public enum Player { - X, - O -} diff --git a/src/main/java/de/icaotix/ultimatetictactoe/model/TicTacToeGame.java b/src/main/java/de/icaotix/ultimatetictactoe/model/TicTacToeGame.java index 6ccff98..b7f8a11 100644 --- a/src/main/java/de/icaotix/ultimatetictactoe/model/TicTacToeGame.java +++ b/src/main/java/de/icaotix/ultimatetictactoe/model/TicTacToeGame.java @@ -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 getAvailableFields() { final LinkedList 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 interestingStates = new HashSet<>(); + final var interestingStates = new HashSet(); 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()); - } - } diff --git a/src/main/java/de/icaotix/ultimatetictactoe/model/UltimateTicTacToe.java b/src/main/java/de/icaotix/ultimatetictactoe/model/UltimateTicTacToe.java index ee67b77..205a472 100644 --- a/src/main/java/de/icaotix/ultimatetictactoe/model/UltimateTicTacToe.java +++ b/src/main/java/de/icaotix/ultimatetictactoe/model/UltimateTicTacToe.java @@ -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 interestingStates = new HashSet<>(); + final var interestingStates = new HashSet(); interestingStates.add(this.masterGameStates[winningCombination[0]]); interestingStates.add(this.masterGameStates[winningCombination[1]]); interestingStates.add(this.masterGameStates[winningCombination[2]]); diff --git a/src/main/java/de/icaotix/ultimatetictactoe/model/CellState.java b/src/main/java/de/icaotix/ultimatetictactoe/model/definitions/CellState.java similarity index 76% rename from src/main/java/de/icaotix/ultimatetictactoe/model/CellState.java rename to src/main/java/de/icaotix/ultimatetictactoe/model/definitions/CellState.java index 47d014f..7317168 100644 --- a/src/main/java/de/icaotix/ultimatetictactoe/model/CellState.java +++ b/src/main/java/de/icaotix/ultimatetictactoe/model/definitions/CellState.java @@ -1,4 +1,4 @@ -package de.icaotix.ultimatetictactoe.model; +package de.icaotix.ultimatetictactoe.model.definitions; public enum CellState { X("X"), diff --git a/src/main/java/de/icaotix/ultimatetictactoe/model/GameState.java b/src/main/java/de/icaotix/ultimatetictactoe/model/definitions/GameState.java similarity index 80% rename from src/main/java/de/icaotix/ultimatetictactoe/model/GameState.java rename to src/main/java/de/icaotix/ultimatetictactoe/model/definitions/GameState.java index 6fc113d..ed4afef 100644 --- a/src/main/java/de/icaotix/ultimatetictactoe/model/GameState.java +++ b/src/main/java/de/icaotix/ultimatetictactoe/model/definitions/GameState.java @@ -1,4 +1,4 @@ -package de.icaotix.ultimatetictactoe.model; +package de.icaotix.ultimatetictactoe.model.definitions; public enum GameState { X_WON("X won"), diff --git a/src/main/java/de/icaotix/ultimatetictactoe/model/definitions/Player.java b/src/main/java/de/icaotix/ultimatetictactoe/model/definitions/Player.java new file mode 100644 index 0000000..3cf6161 --- /dev/null +++ b/src/main/java/de/icaotix/ultimatetictactoe/model/definitions/Player.java @@ -0,0 +1,6 @@ +package de.icaotix.ultimatetictactoe.model.definitions; + +public enum Player { + X, + O +} diff --git a/src/main/java/de/icaotix/ultimatetictactoe/view/UltimateTicTacToeView.java b/src/main/java/de/icaotix/ultimatetictactoe/view/UltimateTicTacToeView.java index 301684e..5f0e1d5 100644 --- a/src/main/java/de/icaotix/ultimatetictactoe/view/UltimateTicTacToeView.java +++ b/src/main/java/de/icaotix/ultimatetictactoe/view/UltimateTicTacToeView.java @@ -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); } } diff --git a/src/main/java/de/icaotix/ultimatetictactoe/viewmodel/UltimateTicTacToePanelViewModel.java b/src/main/java/de/icaotix/ultimatetictactoe/viewmodel/UltimateTicTacToePanelViewModel.java index 7ce496c..a45ce78 100644 --- a/src/main/java/de/icaotix/ultimatetictactoe/viewmodel/UltimateTicTacToePanelViewModel.java +++ b/src/main/java/de/icaotix/ultimatetictactoe/viewmodel/UltimateTicTacToePanelViewModel.java @@ -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 gameResultCallback; - private Consumer currentPlayerCallback; + private Consumer 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 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 currentPlayerCallback) { + public void setCurrentPlayerCallback(Consumer currentPlayerCallback) { this.currentPlayerCallback = currentPlayerCallback; } } diff --git a/src/main/java/de/icaotix/ultimatetictactoe/viewmodel/ViewModelFactory.java b/src/main/java/de/icaotix/ultimatetictactoe/viewmodel/ViewModelFactory.java new file mode 100644 index 0000000..b8f3f72 --- /dev/null +++ b/src/main/java/de/icaotix/ultimatetictactoe/viewmodel/ViewModelFactory.java @@ -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); + } +}