Design Discussion Notes

Thomas Kennedy

Contents:

1 Tic-Tac-Toe Proper Design

1.1 Step 1: Finding the Pieces

1.1.1 Candidate Classes

 

1.1.2 Candidate Responsibilities

 

1.2 Step 1.5: Finding Duplicates

1.2.1 Pruning Step (Synonym Check) Candidate Classes

 

1.2.2 Pruning Step (Synonym Check) Candidate Responsibilities

 

1.3 Step 2: The Big Picture

This is a simple example: A Single Tic-Tac-Toe Match. I am going to look a some pseudo-code for selected pieces of a single match. Let us look at a single move by a single player:

 
selectedCell <- player1.makeMove()
symbol <- player1.getSymbol()

board.set(selectedCell, symbol)

Let us assume–for the moment–that all players make valid moves all the time. That leaves us with two classes:

It would be really nice if we had some sort of UML Class Diagram to handle this notation–instead of inventing our own notation.

 

@startuml Player : symbol Player : makeMove() Board : getCell(position) Board : setCell(position, symbol) @enduml

The resulting UML Class Diagram is a good start…

A Decent Start

Of course, I need to add the missing move argument to Player::makeMove.

 

A Decent Start

The corresponding PlantUML markup can be written as:

tic-tac-toe-2.puml
@startuml
hide empty members

Player : name
Player : symbol
Player : makeMove(theBoard : Board, theMove)

Board : getCell(position)
Board : setCell(position, symbol)

class Strategy {

}

Player --> Strategy: employs

@enduml

Note: I added hide empty members to collapse all empty attributes and behaviors. I do not particularly like the resulting markup. I am a stickler for consistency. Let us tweak the Player and Board lines.

tic-tac-toe-2-final.puml
@startuml
hide empty members

class Player {
    name
    symbol

    makeMove(theBoard : Board, theMove)
}

class Board {
    getCell(position)
    setCell(position, symbol)
}

class Strategy {

}

Player --> Strategy: employs

@enduml

Let us add the Referee and Game classes to our diagram. I am pretty sure I will include both Game and Referee in my final model. If for some reason I am fundamentally mistaken… I can remove them later.

A Decent Start

tic-tac-toe-3.puml
@startuml
hide empty members

class Player {
    name
    symbol

    makeMove(theBoard : Board, theMove)
}

class Board {
    getCell(position)
    setCell(position, symbol)
}

class Strategy {

}

class Referee {

}

class Game {

}


Player --> Strategy: employs

@enduml

1.4 Step 3: Back to the Lists

Let us revisit our lists of Noun Phrases

 

and Verb Phrases

A Decent Start

2 Tackling Game and Referee

Let us tackle Game and Referee concurrently. We have not tackled:

I argue that Rules are covered by Referee. However, I am not sure. I better make a note to ask the Domain Expert (Question Time).

I also argue that Winner and Loser are not appropriately captured as classes. A Game will have two Players: player1 and player2. It is possible for neither to win (i.e., there is no winner or loser). Let us update Game with this new insight.

A Decent Start

Note that I listed that player1 and player2 are of the type Player. This is appropriate. Player is a domain specific type (i.e., it is a conceptual notion). It is not an implementation detail.

We are now left with

 

Let us tackle Round. I think this is really meant to capture a single two-player turn sequence (i.e., player1 makes a move then player2 makes a move). English is fun… This probably should have been listed as a Verb Phrase in the form: player1 makes a single move followed by player 2. This is defined as one round or turn. Context is key. This is why the candidate lists are merely a starting point.

Let us update our Game class…

A Decent Start

I will apply a similar set of arguments to Win, Loss, and Stalemate. These are probably meant to check the results at the and of a single Game… once the Game is Over. We now have four new operations: isOver, endedWithWin, endedWithLoss, and endedWithStalemate. Let us update out model…

A Decent Start

Wait… I do not like that. How do I know when a win occurs? What about a loss? What about a stalemate? I think there is some sort of process behind these checks. I think this is handled by the Referee.

A Decent Start

I am much happier with this. The Referee checks for the win, loss, or stalemate. The Game merely records if these have occurred. I am comfortable with this logic.

Let us add the relationship between Referee and Board.

A Decent Start

Our PlantUML markup is now:

tic-tac-toe-9.puml
@startuml
hide empty members

class Player {
    name
    symbol

    nextMove(theBoard : Board)
}

class Board {
    getCell(position)
    setCell(position, symbol)
}

class Strategy {

}

class Referee {
    validate(selectedMove, theBoard : Board)
    checkForWin(theBoard: Board)
    checkForLoss(theBoard: Board)
    checkForStalemate(theBoard: Board)
}

class Game {
    player1: Player
    player2: Player

    getWinner() : Player
    getLoser() : Player
    playOneRound()
    isOver()
    endedWithWin()
    endedWithLoss()
    endedWithStalemate()
}

Player --> Strategy: employs
Referee --> Board: examines

@enduml

I do not think a Game Board can exist outside a Game. Let us add the appropriate composition relationship.

A Decent Start

tic-tac-toe-10.puml
@startuml
hide empty members

class Player {
    name
    symbol

    nextMove(theBoard : Board)
}

class Board {
    getCell(position)
    setCell(position, symbol)
}

class Strategy {

}

class Referee {
    validate(selectedMove, theBoard : Board)
    checkForWin(theBoard: Board)
    checkForLoss(theBoard: Board)
    checkForStalemate(theBoard: Board)
}

class Game {
    player1: Player
    player2: Player

    getWinner() : Player
    getLoser() : Player
    playOneRound()
    isOver()
    endedWithWin()
    endedWithLoss()
    endedWithStalemate()
}

Player --> Strategy: employs
Referee --> Board: examines
Game *--Board

@enduml

3 Constructing Useful UML Class Diagrams

We can also say that a Referee is assigned to a Game and that Referee keeps Players honest

A Decent Start

Before we continue… we should probably add isFull to Board. I think the Referee might need this information for the stalemate check.

A Decent Start

This leaves us with a fairly some fairly long PlantUML markup.

tic-tac-toe-12.puml
@startuml
hide empty members

class Player {
    name
    symbol

    nextMove(theBoard : Board)
}

class Board {
    getCell(position)
    setCell(position, symbol)
    isFull()
}

class Strategy {

}

class Referee {
    validate(selectedMove, theBoard : Board)
    checkForWin(theBoard: Board)
    checkForLoss(theBoard: Board)
    checkForStalemate(theBoard: Board)
}

class Game {
    player1: Player
    player2: Player

    getWinner() : Player
    getLoser() : Player
    playOneRound()
    isOver()
    endedWithWin()
    endedWithLoss()
    endedWithStalemate()
}

Player --> Strategy: employs
Referee --> Board: examines
Game *-- Board
Referee --> "2" Player : keeps honest
Referee --> "1" Game : assigned to

@enduml

3.1 Forming Coherent Thoughts

What are we conveying in our current diagram? We are saying everything at once. We only have five (5) classes in this model. Imagine a larger model (i.e., one with at least 12 classes).

Could you make sense of such a digram? The answer is no.

Let us strip out the associations.

A Decent Start

This captures our known Class interfaces and attributes. What do we want to convey? Now… Let us form coherent thoughts!

A Decent Start

This diagram captures how players have a strategy. It is based on this strategy they select their moves. The referee ensures that not invalid moves are made (e.g., that players do not cheat).

A Decent Start

This diagram captures the structure of a single game (i.e., one match between two players on a single board). Each board is fundamentally linked to a single game

3.2 What About Players?

We should probably consider Human and Computer Players. I will leave writing a description as an exercise to the reader.

A Decent Start

This results in our last piece of PlantUML markup.. for now!

tic-tac-toe-16.puml
@startuml
hide empty members

class Player {
    name
    symbol

    nextMove(theBoard : Board)
    isHuman()
    isComputer()
}

Player <|---- HumanPlayer
Player <|---- ComputerPlayer

@enduml

How would our previous C++, Java, and Python design decisions change?

4 Our Final Model

A Decent Start

Nope… this is the last piece of markup. I guess I am a victim of the standard off-by-one error.

tic-tac-toe-17.puml
@startuml
hide empty members

class Player {
    name
    symbol

    nextMove(theBoard : Board)
}

class Board {
    getCell(position)
    setCell(position, symbol)
    isFull()
}

class Strategy {

}

class Referee {
    validate(selectedMove, theBoard : Board)
    checkForWin(theBoard: Board)
    checkForLoss(theBoard: Board)
    checkForStalemate(theBoard: Board)
}

class Game {
    player1: Player
    player2: Player

    getWinner() : Player
    getLoser() : Player
    playOneRound()
    isOver()
    endedWithWin()
    endedWithLoss()
    endedWithStalemate()
}

Player <|---- HumanPlayer
Player <|---- ComputerPlayer

@enduml