I have recently been learning Java, and attempting to write TicTacToe in a clean and concise way. I have found this a challenge, and have made many mistakes that have led me to further appreciate the SOLID principles, particulary the Open/Closed Principle.

The Open/Closed Principle states that your application should be open to extension and closed to modification. This means that it ought be written in such a way that if a feature is added, or a class is extended, existing code needn’t change. An indication that this principle has not be followed is when a change in one part of your application prompts changes in several other places. I created such a problem in my TicTacToe application.

Below is a class called Session. The purpose of Session is to set up a game by creating the players with which to initialise the Game class. In order to create players, the method createPlayersOfType takes a string response of "a", "b", "c" or "d" to prompt the playerCreator to initialise players of a certain class.

The problem with this if/else block in Session for player creation is that if we wanted to add another option, such as a RandomComputerPlayer vs an UnbeatablePlayer, we would have to change something in the Session class, the PlayerCreator class, the Messenger class and the Validation class. A small change in one class would affect several others. This clearly violates the Open/Closed Principle.

So how can we fix it?

Above is a rough diagram of the paths between class methods needed to instantiate a Game with certain types of players. We need to think of a way that a change to one of these methods won’t trigger a change in all the other classes. Session should not know about the different options available for creating different player types. This responsibility should belong to our playerCreator.

So first of all we should remove knowledge of the game options from Session, by removing the if/else block. Session should simply make a call to playerCreator to create the correct type of players, as below:

Now we can think of a way that playerCreator can use different userResponse to create players without using an if/else block. One solution is to create a hashmap, where the keys are different options and the values contain two players of the correct type. Below is an example of a hashmap called gamePlayerOptions. If there are ever more game options added we need simply to add it to the hashmap shown in the constructor.

What about Validation?

We have now removed knowledge of the game options from Session, but that knowledge still exists in the playerCreator, the Validator and the Messenger. Does the Validator need to know about the game options in order to validate? We could instead pass the required regex to match against the game options to the Validator at the time we use them. This means that the playerCreator needs to know what the match requirements are but the Validator need not.

Above we have requested a response from the Validator by passing it the user input, the match requirements regex and the invalid message from the Messenger specific to the game options of the PlayerCreator.

Now if there are any new game options added it is the PlayerCreator that knows about new options and a changed match requirement, but not the Validator at all. The amount of places that need to be changed by a new option has halved from when we started!

Removing Game Option Knowledge from Messenger Class

What if we wanted to only have knowledge of the game options in the PlayerCreator and nowhere else? We can use another data structure to store the possible options and just pass them to the Messenger when it is time to list them.

First we would create a List<String> to store all the strings of options, like so:

However, the user is initially prompted for a response to the game options in the Session class. This prompt should instead be introduced in the PlayerCreator class. The PlayerCreator passes the Messenger method askGameType the list of options that it stores.

In the PlayerCreator class:

And in the Messenger class: