Object-Oriented Design Interview

Course Content

Object-Oriented Design Interview

14 sections · 29 lessons

Tic Tac Toe: requirements and where responsibilities belong


Tic tac toe is the smallest problem in the course, and that is what makes it a trap. With no domain and no scale to hide behind, the whole score comes down to where responsibilities are placed. This lesson covers the prompt, the requirements, and the two placement decisions the problem is really about.

Four questions that make a trivial game realDesign tic tac toe3 by 3, or N by N?Two humans, or an AI?Is undo required?One game or a session?
Asking about N by N before writing a line is what separates this from a homework exercise.

Why a trivial game is a real interview question

Tic tac toe is given to junior candidates constantly, and the reason is that it removes every excuse. There is no domain to learn, no ambiguity about the rules, and no scale. What is left is: can you place responsibilities correctly, and can you make one small optimisation cleanly?

That makes it dangerous in a specific way. Candidates relax, write a Game class with a 2D array and a hundred-line play() method, and produce something that works perfectly and demonstrates nothing. A small problem still deserves a real design — that is the lesson, and interviewers use this problem precisely because it separates people who model out of habit from people who model only when forced.

The four questions that change the model

1. "Fixed 3×3, or N×N with K in a row?" This is the question that matters most. A 3×3 board can be won by checking eight lines of three — you can hard-code it and nobody will care. An N×N board with K in a row makes the win check an actual algorithm, and it is where the interesting code in Win detection, in code comes from. Ask, and propose N×N even if they say 3×3: "I'll design for N×N with K in a row so the win check generalises — it costs nothing extra."

2. "Two human players, or a computer opponent?" A computer opponent introduces the Strategy pattern (The seven patterns that actually appear) and is the natural extension. Assume two humans for the core, with the interface shaped so a bot slots in.

3. "Is undo needed?" Undo is the requirement that justifies keeping a move history, and without it a move list is speculative. Ask before building one.

4. "Does the game need to be replayable or recorded?" Replay means the move log is the source of truth and the board is derived from it — a genuinely different emphasis. Assume not, but mention it.

Assumptions this lesson makes

  • An N×N board with K in a row to win; the defaults are N=3, K=3.
  • Two players alternating turns, each with a distinct piece.
  • Moves are validated: inside the board, on an empty cell, and by the player whose turn it is.
  • The game ends on a win or when the board is full (a draw).
  • Single-threaded, one game per instance. Concurrency is not part of this problem, and saying so is better than inventing it.

Requirements

Functional requirements

Text
1. Start a game with two players and a board of size N, needing K in a row2. Accept a move: a player, a row, and a column3. Validate the move: in bounds, cell empty, and the correct player's turn4. Detect a win after each move5. Detect a draw when the board is full with no winner6. Report the game's outcome and stop accepting moves

Requirement 3 has three separate checks in it, and a design that handles them in three different places is one of the ways this problem goes wrong. Finding the objects, below, puts them where they belong.

Requirement three has three checks in itXOOOX---Xc0c1c2r0r1r2Validity, win, and draw are all decided from the cell just filled.
Only the four lines through the placed cell can have completed, so the whole board is never rescanned.

Non-functional requirements

N1. Win detection should not rescan the whole board after every move. On a 3×3 board this is meaningless — 24 reads is nothing. On a 15×15 Gomoku board with K=5, a full scan per move is about 900 cell reads while the incremental check is about 4. This requirement exists to give the round something to optimise, and the optimisation is genuinely satisfying, which is why Win detection, in code exists.

N2. Adding a new player type (a bot) must not modify the game loop.

N3. The rules must be data, not branches. N and K are configuration; a design that hard-codes 3 in eight places cannot become Connect Four.

What is cut

Text
CUT (named, not forgotten)- Networking, matchmaking, and lobbies- Persistence between sessions- A user interface — the design exposes a method, not a screen- Scoring across multiple games, tournaments, ratings

Say: "I'll expose makeMove(player, row, col) and let a console or a web layer call it. The interface is the boundary; I'm not designing a screen."

The scale sentence

There is no scale here, and saying so is better than pretending:

"There's no performance story in a 3×3 game — everything is instant. The only place efficiency becomes real is if we generalise to a large board, which is why I want to write the win check as if N were large. Otherwise I'd be optimising nothing."

That is honest and it sets up Win detection, in code. Inventing a scale problem where none exists is a different failure mode, and interviewers notice it.

Finding the objects

The candidate nouns

Game, board, cell, player, move, piece, symbol, turn, winner, result, position.

The two placement decisions this problem is aboutBoard owns• The grid and every cell's contents• Whether a placement is legal• Whether the last move ended itGame owns• Whose turn it is right now• The two players and their symbols• Starting, ending, and the result
Put the win check on Game and it must read the whole board through a getter — the god class begins.

Filtering them

CandidateVerdictReason
GameClassOwns turn order and the game's status
BoardClassOwns the grid and, as argued below, the win check
CellBorderlineA cell holds one nullable piece and nothing else
PlayerClassA name and a piece; gains behaviour when bots arrive
MoveClassNeeded for undo and replay; a small value object
PieceEnumX, O — a closed set with no behaviour
Turn, WinnerNot classesState on Game, not entities

On Cell. A separate Cell class holding one Piece field is defensible and mostly ceremony. A Piece[][] where null means empty is honest and shorter. Pick either, and say why: "I'll use a Piece[][] with null for empty rather than a Cell class — a cell has no behaviour of its own. If cells gained behaviour, like being blocked or scoring differently, I'd promote it to a class." Naming the condition under which you would change your mind is the part that scores.

The two placement decisions this problem is about

Decision one: Player must not hold the board.

A common first attempt gives Player a makeMove() method that reaches into the board:

Java
// The version to rejectpublic class Player {    private Board board;    public void makeMove(int row, int col) {        board.place(row, col, piece);       // the player mutates shared state directly    }}

Three problems. Every player holds a reference to shared mutable state, so nothing controls turn order — two players can both move. Validation has no single home. And when a bot arrives, it holds a board it can mutate at will rather than being asked for a decision.

The correct shape: a player decides, the game applies.

Java
public class Player {    private final String name;    private final Piece piece;    // A human player is asked for a move by the interface layer.    // A bot implements a strategy that returns a move given a read-only board view.}

Game.makeMove(player, row, col) is the only path that mutates anything. Turn order, validity, and outcome are all checked in one place.

Decision two: the win check belongs to Board, not Game.

The rule from Phase 4: assigning responsibilities is put behaviour with the data it needs. Win detection reads the grid and nothing else — no turn order, no player names, no game status. Put it on Game and Game must either read the board's internals (breaking encapsulation) or the board must expose its grid, which is the same thing with more steps.

Put it on Board and the incremental counters in Win detection, in code live beside the grid they summarise — which is what makes that optimisation possible at all.

One sentence per class

Text
Game   — enforces turn order and reports the game's outcome.Board  — holds the grid and answers whether the last move won.Player — identifies one participant and their piece.Move   — records one placement: player, row, column, and sequence number.Piece  — which mark occupies a cell.

Five, all passing the one-sentence test, for a problem most people solve with one class and an array.