Before you start
Tic-Tac-Toe is the smallest problem in this module. On purpose — this is where you practice the discipline of not over-designing.
The over-engineering trap
Every design-pattern student wants to use patterns everywhere. Tic-Tac-Toe is small enough that Factory / Strategy / State are all OVERKILL. Plain classes will do fine.
Your design.md's Patterns section can — and should — say:
"I didn't use any pattern from modules 1-3. Plain classes were the right choice for a game this small. Adding patterns here would add work without any payoff. If the requirements grew (multiple game modes, an AI opponent, replays), I'd revisit."
That's an interview-quality answer. Reaching for patterns just to look like you're using them is a common junior mistake.
Where the real design decisions live
Small problem, but real decisions:
- Win detection. Compute all winning lines at
__init__(rows + columns + diagonals). Check after every move. OR: check only lines that include the just-placed mark. Which is simpler? Which is faster? design.md is where you make the call. - Current player. Store an explicit
_currentfield OR compute it from the move count. The second is cheaper and can't get out of sync. Argue your choice. - Winner state. Store an explicit
_winnerfield OR compute it from the board every time. Storing is simpler; computing avoids duplicating info but is slower. Which fits?
Every one of these is a small design decision. An interviewer will ask "why did you do it that way?" for at least one. Your design.md is your rehearsal.
The rules discipline
Even for a small problem, rules exist:
- Never two marks in one cell
- Current player alternates strictly (X then O then X...)
- Game refuses moves after it ends
For each, say WHERE your code enforces it. If the
answer is "my play() method checks", say so — the
point is that ONE place enforces it, not that every
caller has to check.