Introduction
One of the features that I was amazed with in Java is the way of handling events, especially in swing and JavaFX GUI applications. When I started writing my own Java libraries, I needed to implement something similar to that way. After I took a course about software design, I learned something interesting. It turns that this feature is actually something called Observer Design Pattern.Topics Covered
In this blog post, I will cover the following topics:- Defenition of Design Pattern.
- The Observer Design Pattern.
- Implementing The Observer Design Pattern in Java.
- A Summary of All The Steps for Implementing The Observer Design Pattern.
What is a Design Pattern?
In software engineering, a design pattern is simply a solution to a common problem in software design that can be used to solve the problem in the most effective way. An example of a design problem is how to make one instance of an object the whole life time of a program. The solution to this problem is to use one of the easiest-to-implement design patterns, the Singleton Design Pattern.The Observer Design Pattern
The observer design pattern is also used to solve one of the common software design issues. To illustrate the problem that this design pattern solves, let's start by stating a simple example that we will implement it later in Java.'X' 'O' Game
There are more simpler examples out there on the internet but the 'X' 'O' Game is interesting to see how we can use this design pattern in designing games. The simplest example we can think of is that an object says 'hi!' and the other replay with the same thing.How The Game Works
The idea of the game is as follows, we have a grid of size 3x3. Two players can play at any time. The first player places 'O' on one cell. After that, the second player places 'X' on another Cell. The first one who have a line of 3 'X's or 'O's wins the game (Horizontal, vertical or diagonal). Note that it is possible to implement the game without the use of observer design pattern. But we will be using it as an example.The Observer Place in The Game
The observer design pattern is used to implement a way for objects to listen to each other. Basically an object 'A' is waiting for an event to be done by object 'B' in order to perform some actions.In the game, we have 3 important objects, The first player, the second player and the game board (or the paper). The two players are the ones who is doing actions. The the game board will be observing the actions.
One thing that we have to consider is which player will play next. Because once the first player finish his turn, the second one come in and play. We should not allow the first player to play again till the second player finish his turn. Also after each move, we should check if there is a winner or not.
The given problems can be solved by using the observer design pattern. The game board is interested on the moves each player makes. When a player finish his turn, the game board will check his state. If there is a winner, the game finishes. If no winner, it is the time for next player to play. If the game board is filed with 'X's and 'O's, the game finishes and it is a tie.
This means we have one observer (The game board) and two subjects (The players).
This is how the observer design pattern works in general. |
Implementing Observer Design Pattern in Java
Since we are interested in implementing the design pattern and not the game, we will be using a semi-complete code to do that. The code can be found in my Github account under the project XOGame. There are two folders, one contains the full code and the other which has a partially implemented code. We will be working on the partially implemented version of the game.Understanding The Code
The first thing to note is that we have 3 main java files, each file represents one class:- The Player Class
- The GameBoard Class
- The Game Class
The Player Class
This class represent one player in our game. In the observer design pattern, we call this class the subject. The subject is simply the object that we are interested on observing it. The event that we are interested on observing is the event of placing 'X' or 'O' on the game board. When we go to the method play(int,int), we can see that the body is empty which means the observer design pattern will have a role on that method.The GameBoard Class
In simple words, this class has the main game logic. It represents a paper where the players will draw the grid for the X-O Game. The grid is usually of size 3x3 but in some variations of the game it can be larger. The game board will be observing the players as they place 'X' or 'O'. When a player finish his turn, the game board will do the following, First, it checks if the move is valid or not. After that, it checks if the player has win the game or not after his move and finally, it switches turns (If it was P1 who has played, P2 will be next).The Game Class
This class implements the Runnable interface. It acts as the main engine for running the game. It has the functionality to get the input from the players. Also it has the functionality to validate the input.This flow chart shows the flow of events when running the game. |
The Observer
The first building block of the observer design pattern is the observer. In java language, the observer is usually an interface that has method signatures only. In java language terminology its known as a listener. So, we will create a new java interface. Let's call the new file PlayerListener. From the name of the interface, we can infer that the class that will implement the interface will act as an observer (or listener) to the player.Java Code
1
2
3
4
public interface PlayerListener {
}
Java Code
1
2
3
4
public interface PlayerListener {
public boolean play(Player source, int rowIndex, int colIndex);
}
Attaching Observer to The Subject
Remember that the subject in the game is the Player object. In java terminology, the subject is called listenable, and attaching an observer to the subject is called adding listener. The first thing we will do is to add extra attribute to the class Player. The attribute will be of type ArrayList<PlayerListener>. Simply we create an array list that will hold all the objects that will observe the player.Java Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.util.ArrayList;
public class Player {
ArrayList<PlayerListener> listeners;
//other attributes...
public Player(int id){
this.listeners = new ArrayList<>();
//other code...
}
}
Java Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.util.ArrayList;
public class Player {
ArrayList<PlayerListener> listeners;
//other attributes...
public Player(int id){
this.listeners = new ArrayList<>();
//other code...
}
public void addPlayerListener(PlayerListener listener){
if(listener != null){
this.listeners.add(listener);
}
}
//other code...
}
Notifying Observers
The final thing that we will do with the Player class is to add the code inside the method play(int,int). In this method, we will only notify the observers about the player's move. We will do that using enhanced for-loop. In reality, we might have other actions that will be performed by the subject before notifying observers. Usually the notify step is the last step on the action that the subject performs.Java Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import java.util.ArrayList;
public class Player {
ArrayList<PlayerListener> listeners;
//other attributes...
public Player(int id){
this.listeners = new ArrayList<>();
//other code...
}
public void addPlayerListener(PlayerListener listener){
if(listener != null){
this.listeners.add(listener);
}
}
public void play(int rowIndex, int columnIndex){
for(PlayerListener l : this.listeners){
l.play(this,this.x_or_o,rowIndex,columnIndex);
}
}
//other code...
}
Implementing the Interface PlayerListener
As we have said before, the observer in our game is the class GameBoard. What we need to do is to make the given class implement the interface PlayerListener. Keep in mind that any class is interested on observing the player must implement the interface PlayerListener which acts as an observer.Java Code
1
2
3
4
5
6
7
8
9
10
11
12
public class GameBoard implements PlayerListener{
//other code...
@Override
public void play(Player source, int rowIndex, int columnIndex){
}
//other code...
}
- Check if the given place for the move is empty (No one has placed 'X' or 'O' before in it).
- If empty, Do the following:
- Place the character that the player is using for his move in the given place.
- Update turn.
- Check if the source player is a winner after his move.
- If not empty, display error message.
Java Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class GameBoard implements PlayerListener{
//other code...
@Override
public void play(Player source, int rowIndex, int columnIndex){
//first check if the place that the player will play on is empty.
if(this.gameGrid[rowIndex][columnIndex] == null){
//switch turns
if(turn == 1){
turn = 2;
}
else if(turn == 2){
turn = 1;
}
//place the 'X' or 'O' on the grid
this.gameGrid[rowIndex][colIndex] = source.getChar();
//check if the source player is the winner
if(this.isWinner()){
this.winner = source;
}
}
else{
System.out.println("Choose Another Place to Play!");
}
 }
}
Attaching The Observer to The Subject
The process of attaching the GameBoard to the players is performed inside the class Game. To be specific, inside the constructor. After we initialize the class attributes, the final step is to attach the GameBoard to both players.Java Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Game{
//class attributes...
public Game(){
this.gameBoard = new GameBoard();
this.firstPlayer = new Player(1);
this.secondPlayer = new Player(2);
this.firstPlayer.setChar('X');
this.secondPlayer.setChar('O');
//adding the game board as a listener to both players.
this.firstPlayer.addPlayerListener(this.gameBoard);
this.secondPlayer.addPlayerListener(this.gameBoard);
}
}
Java Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class Game{
//other code...
public void run(){
//other code...
/*
when the method 'play() of the class 'Player' is called,
the game board is notifyed since it is observing the player.
*/
if(turn == 1){
this.firstPlayer.play(rowIndex, columnIndex);
}
else{
this.secondPlayer.play(rowIndex, columnIndex);
}
//other code...
}
}
Summary
Implementing the observer design pattern in java language involve the following steps:- Creating the observer (Java Interface).
- Creating a list on the subject to maintain the observers.
- Add a method inside the subject that can be used to attache observers.
- Notify all the observers using a loop once the event of interest accrues.
- If a class is interested on observing the subject, he must implement the interface that represents the observer of that specific subject.
No comments:
Post a Comment
Feel free to write any thing in your mind here 😉