Another example of Object Theater
Mastermind
This year I gave my Web Applications students the task of writing a Mastermind game. You can see some of their work here, here and here. The game works like this: the computer starts a new game by inventing a random secret code, composed of 4 digits from 1 to 6. For instance: 5414.
The player must deduce what is the secret code by trying guesses. To continue the example if my first guess is 1234, the computer will answer “-+” which means that I got one number right (+) and another number is present in the secret code but in a different position (-). Of course, I don’t know which! I can then try more guesses, until I have enough clues to guess right.
You get a score for each completed game, that is equal to the number of guesses. Of course, the lower the score, the better.
A player will earn a score that is the average of all the games he or she completed.
Procedural!
Teaching Object-Oriented programming was not one of the goals of the course. Therefore, most programs I got were very procedural. (Please note that the example that follows is from a good student and his work earned a very high score in my course. It was a good web application, even though it was not object-oriented).
I’d like to show an example of procedural code. This is a controller object that handles a “guess”.
public void guess() throws IOException {
String gameId = request.getParameter("game_id");
String code = gamesRepository.findSecretCode(gameId);
String guess = request.getParameter("guess");
String answer = compareCodes(code, guess);
String player = gamesRepository.find_game_player(gameId);
guessesRepository.createGuess(gameId, player, guess, answer);
gamesRepository.incrementGameScore(gameId);
// if game is won
if(answer.equals("++++")){
gamesRepository.setFinished(request.getParameter("game_id"));
int oldScore = gamesRepository.getPlayerScore(player);
playersRepository.addFinishedGame(player, oldScore);
}
// return json data
response.getWriter().write(toJson("answer", answer));
}
This is classic procedural code; the game logic is found in the controller (method compareCodes()
) and the repositories (three repositories!). There are no domain objects.
The database structure is something like
1 * 1 *
player -------- game -------- guesses
The gameRepository adds a row to the games table when a new game is added. The guessesRepository adds a row to the guesses table when a new guess is guessed. The controller must take care to call the proper repositories at the right time. The controller “knows” the database structure; if the database structure changes, the controller code will probably also change.
Object-Oriented
What I’d like to do instead is
- Domain objects that handle all the game logic
- No logic in the repositories or the controller
- Just one repository is enough, thank you. The repository should take care of adding rows to the proper tables.
The domain object should probably be the MasterMind game.
game.guess(request.getParameter("guess"));
The “guess” message is what sets the domain logic in motion. The controller should not need to know anything else.
Q. What if the game is won? Who updates the player’s score?
A. The game object should do that.
Q. Where do we see that the game updates the player’s score?
A. Not in the controller. The controller does not know or care. Handling victories is something that is done in the game object.
Q. Really! How do we update the player’s score?
A. The game probably knows its player, and tells it to update its score if the game is won.
Q. How does the game get a reference to its player?
A. The controller does not know. But see next question.
Q. Where do we get the game object from?
A. From a repository, of course. We suppose that the repository will return it with all the dependencies that it needs to have. If the game needs a reference to its player, the repository must take care to set it up.
String gameId = request.getParameter("game_id");
Game game = gamesRepository.findGame(gameId);
game.guess(request.getParameter("guess"));
Q. How is the state of the game persisted?
A. By asking the repository to save the game.
// Here we are in the infrastructure world
String gameId = request.getParameter("game_id");
Game game = gamesRepository.findGame(gameId);
// Now we pass to the realm of pure domain logic
game.guess(request.getParameter("guess"));
// And now we return to the infrastructure world
gamesRepository.save(game);
response.getWriter().writer(toJson(game));
So we have seen another example of object theater. The infrastructure details are dealt with before and after the main action. The main action is sending “guess” to the game object. There is where the functional requirements is dealt with. Before that, and after that, is infrastructure code that deals with non-functional, performance requirements.