Tic Tac Toe with SOLID principles I am making a Tic Tac Toe game to be played in the console either against another human or a simple AI. It is a little rough but it works. What concerns me is how well it adheres to SOLID and other design principles as this is my first time using them in practice. I would appreciate a review of how my classes are structured and any advice. I am sharing a link from GitHub because there is too much code to post everything here.

These are some of the largest classes and give me an uneasy feeling. 

GameBoard.class - has the game board and holds players' markers

package board;

import java.util.ArrayList;


public class GameBoard {

    private Cell[] board;

    private ArrayList&lt;Integer&gt; availableSpots;

    public GameBoard(int n) {
        availableSpots = new ArrayList&lt;Integer&gt;();

        //the game board
        board = new Cell[n];
        for (int i = 0; i &lt; n; i++) {
            board[i] = new Cell();
        }

        //list of initial open cells
        for (int i = 0; i &lt; 9; i++) {
            availableSpots.add(i);
        }
    }


    public Cell[] getBoard() {
        return board;
    }

    public void addMarker(int n, String s) {
        if (availableSpots.contains(n)) {
            board[n].setMarker(s);
            removeSpotFromList(n);
            //System.out.println(availableSpots.size());
        }
        else {
                System.out.println("Cell not available");

            }
    }

    public String getCellValue(int n) {
        return board[n].getMarker();
    }

    public ArrayList&lt;Integer&gt; getAvailableSpots() {
        return availableSpots;
    }

    //remove number from arraylist
    public void removeSpotFromList(int n) {
        availableSpots.set(n, null);
        availableSpots.trimToSize();
        for (int i=0; i&lt;availableSpots.size(); i++) {
            //System.out.println(availableSpots.get(i) + ", ");
        }
    }

}


Game.class - switches between players and calls each turn

import board.GameBoard;
import board.PrintBoard;
import consuleInput.InputReader;
import import players.Playable;
import players.PlayerGroup;


public class Game {

    public static void play() {
        Modes modes =  new Modes();
        InputReader ir = new InputReader();
        GameBoard bg = new GameBoard(9);
        PrintBoard pb = new PrintBoard();
        PlayerGroup pg = new PlayerGroup();
        GameStateChecker gsc = new GameStateChecker();
        System.out.println("choose a mode:");
        System.out.println("1. Player vs player");
        System.out.println("2. player v computer");
        System.out.println("3. computer vs computer");

        int mode =  ir.toIntegerReader("type below");

        modes.chooseMode(mode, pg);

        pb.print(bg);

        int counter = 9;
        Playable currentPlayer;
        boolean hasWinner = false;
        int player = 0;
        //int choice;
        do{
            currentPlayer = pg.getPlayer(player);
            //int choice = ir.toIntegerReader("pick a square (0-8):");
            bg.addMarker(currentPlayer.playerMove(bg.getAvailableSpots()), currentPlayer.getMarker());
            pb.print(bg);
            hasWinner = gsc.checkPatterns(bg, currentPlayer);
            //System.out.println("is there a winner " + hasWinner);
            counter--;
            if (player == 0) {
                player = 1;
            }
            else {
                player = 0;
            }
            /*
            currentPlayer = pg.getPlayer(1);
            choice = ir.toIntegerReader("pick a square (0-8):");
            bg.addMarker(choice, currentPlayer.getMarker());
            pb.print(bg);
            hasWinner = gsc.checkPatterns(bg, currentPlayer);
            counter--;
            */
        }
        while (!hasWinner);


    }
}
Aside from RobAu's comment, I think the addMarker method can be simplified into:

board[n].setMarker(s);


By delegating the checking the availability of the cell to the Cell class. I think it would be best to define your own exception extending from IllegalArgumentException.

public void setMarker(String marker) {
    if(!marker.equals(" "))
        throw new CellIsNotAvailableException();
    this.marker = marker;
}


Another thing I want to point out is the way you name your methods and variables. One should always name their methods and variables properly such that it won't be neccessary to add a comment explaining what these things do (It's fine to write comments explaining why they exist though). As much as possible, Express yourself in code! Take addMarker for example. I think addMarker would be better written as markCellWithMarker(int cellNumber, String marker) (I consider X's and O's as tokens to be used in the game), and getCellValue as getMarkerAt(int cellNumber).

Applying these changes, here's my refactored version of the GameBoard Class:

public class GameBoard {

    private Cell[] board;

    public GameBoard(int numberOfCells) {
        board = new Cell[numberOfCells];
        for (int i = 0; i &lt; numberOfCells; i++) {
            board[i] = new Cell();
        }
    }

    public void markCellWithMarker(int cellNumber, String token) {
        board[cellNumber].setMarker(token);
    }

    public String getMarkerAt(int cellNumber) {
        return board[cellNumber].getMarker();
    }
}


As for the Game class, it doesn't really justify its existence as it is right now. It contains only one method that is supposed to run a game (if I read your code right). Refactoring it into this might justify its existence:

public class Game {
    private GameBoard gameBoard;
    private Player[] players;
    private int turnNumber;

    //In case you want a two player game.
    public Game(int numberOfCells, Player player1, player player2){
        gameBoard = new GameBoard(numberOfCells);
        setFirstTurnPlayer(player1, player2);
    }

    //In case you want a single player game against the computer. 
    public Game(int numberOfCells, Player player){
        gameBoard = new GameBoard(numberOfCells);
        Player computer = new Player("computer");
        setFirstTurnPlayer(player1, computer);
    }

    private void setFirstTurnPlayer(Player player1, Player player2){
        //It's up to you how you decide the turnorder: die roll, coin toss or something.
        //The first one in the player array is considered as the turn player.
    }

    public String getTurnPlayer(){
        return players[turnNumber%2].getName();
    }

    public void markCellWithMarker(int cellNumber){
        String markerToPlace = players[turnNumber%2].getPlayerMarker()
        gameBoard.markCellWithMarker(cellNumber, markerToPlace)
        checkIfTurnPlayerWinsTheGame()
        turnNumber++;
    }

    //GameStateChecker code moved here. I think this one can be made shorter and more readable.
    public void checkIfTurnPlayerWinsTheGame(){
        //Rename currentPlayer as turnPlayer
        Player currentPlayer = players[turnNumber%2]
        if ((board[0].getMarker().equals(currentPlayer.getMarker()) &amp;&amp; board[1].getMarker().equals(currentPlayer.getMarker()) &amp;&amp; board[2].getMarker().equals(currentPlayer.getMarker())) ||
            (board[3].getMarker().equals(currentPlayer.getMarker()) &amp;&amp; board[4].getMarker().equals(currentPlayer.getMarker()) &amp;&amp; board[5].getMarker().equals(currentPlayer.getMarker())) ||
            (board[6].getMarker().equals(currentPlayer.getMarker()) &amp;&amp; board[7].getMarker().equals(currentPlayer.getMarker()) &amp;&amp; board[8].getMarker().equals(currentPlayer.getMarker())) ||
            (board[0].getMarker().equals(currentPlayer.getMarker()) &amp;&amp; board[3].getMarker().equals(currentPlayer.getMarker()) &amp;&amp; board[6].getMarker().equals(currentPlayer.getMarker())) ||
            (board[1].getMarker().equals(currentPlayer.getMarker()) &amp;&amp; board[4].getMarker().equals(currentPlayer.getMarker()) &amp;&amp; board[5].getMarker().equals(currentPlayer.getMarker())) ||
            (board[2].getMarker().equals(currentPlayer.getMarker()) &amp;&amp; board[5].getMarker().equals(currentPlayer.getMarker()) &amp;&amp; board[8].getMarker().equals(currentPlayer.getMarker())) ||
            (board[0].getMarker().equals(currentPlayer.getMarker()) &amp;&amp; board[4].getMarker().equals(currentPlayer.getMarker()) &amp;&amp; board[8].getMarker().equals(currentPlayer.getMarker())) ||
            (board[2].getMarker().equals(currentPlayer.getMarker()) &amp;&amp; board[4].getMarker().equals(currentPlayer.getMarker()) &amp;&amp; board[6].getMarker().equals(currentPlayer.getMarker()))) 
        throw new GameIsOverException(currentPlayer.getName() + " wins!")
}


Here's the code I wrote for a game called ConnectFour which I think is a bit similar to tic-tac-toe. This may help you see some of the things I mentioned here on proper naming and stuff.