Skip to content

“Observer” pattern

“Observer” pattern

This pattern defines one – to – many relations between objects. If observed object changes its state all dependent objects will receive notification and will be automatically updated.

Concerning the topic „Strategy” pattern, the example in this article also will be about RPG game system. In the classic RPG where the view was izometric, we had the possibility to control the characters of our team – one or few at once. We can check the „Observer” pattern basing on this example. We are the player and we want to give requests to our characters in order to force them to go in the desired direction. We can add and delete the characters which we want to control. When the order is given, the observers should execute the request.

Plan:
1. We will create the Subject interface which contains methods: adding, deleting and notifying objects. Player class will implement this interface.
2. We will also create ObservedCharacter interface containing the update method for observers. Three classes that represent characters will be created: Warrior, Mage and Thief.
3. Moreover, we will create Printing interface that will be used do print data in the console. Also, the enum type TeamDirection will be created for determining the direction in which the team should go.
4. Finally, the test code will be written.

Subject interface with three methods concerning adding, deleting and notifying observers.

public interface Subject {

void registerObserver(ObservedCharacter o);
void deleteObserver(ObservedCharacter o);
void notifyObservers();
}

Player class implementing Subject interface, containing definitions of the above mentioned methods and also containing a setter method for defining the character’s direction.

import java.util.ArrayList;

public class Player implements Subject {
private ArrayList<ObservedCharacter> observers;
private TeamDirection teamDirection;

public Player() {
observers = new ArrayList<>();
}

public void registerObserver(ObservedCharacter o) {
observers.add(o);
}

public void deleteObserver(ObservedCharacter o) {
int i = observers.indexOf(o);
if (i >= 0) observers.remove(i);
}

public void notifyObservers() {
for (int i = 0; i < observers.size(); i++) {
ObservedCharacter observer = observers.get(i);
observer.update(teamDirection);
}
}

public void directionHasChanged() {
notifyObservers();
}

public void setTeamDirection(TeamDirection direction) {
this.teamDirection = direction;
directionHasChanged();
}
}

Interface containing update method for objects.

public interface ObservedCharacter {

void update(TeamDirection direction);
}

Interface containing method for printing text in the console.

public interface Printing {

void printDestination();
}

The character class implementing the above interfaces containing methods for updating the object’s state and for printing the text. After creating the instantion of the class the object is added to the observers list. The below shown example is for the Warrior class. The Mage and the Thief classes are the same, only the class and constructor names are different.

public class Warrior implements ObservedCharacter, Printing {
private TeamDirection direction;
private String name;

public Warrior(Subject Player, String name) {
Player.registerObserver(this);
this.name = name;
}

public void update(TeamDirection direction) {
this.direction = direction;
printDestination();
}

public void printDestination() {
System.out.println(name + " goes "+ TeamDirection.getDirection(direction));
}
}

The enum type determining the directions for giving orders to the characters.

public enum TeamDirection {
LEFT, RIGHT, FORWARD, BACKWARD;

public static String getDirection(TeamDirection direction) {
switch(direction) {
case LEFT: return "left";
case RIGHT: return "right";
case FORWARD: return "forward";
case BACKWARD: return "backward";
default: return null;
}
}
}

The test code which contains adding, deleting and notifying observers.

public class ObserverPatternTest {

public static void main(String[] args) {
Player player = new Player();

Warrior warrior_1 = new Warrior(player, "Warrior 1");
Warrior warrior_2 = new Warrior(player, "Warrior 2");
Mage mage = new Mage(player, "Mage");
Thief thief = new Thief(player, "Thief");

player.setTeamDirection(TeamDirection.LEFT);
System.out.println("");

player.deleteObserver(warrior_1);
player.deleteObserver(mage);
player.deleteObserver(thief);
player.setTeamDirection(TeamDirection.RIGHT);
System.out.println("");

player.registerObserver(mage);
player.deleteObserver(warrior_2);
player.setTeamDirection(TeamDirection.FORWARD);
System.out.println("");

player.registerObserver(thief);
player.setTeamDirection(TeamDirection.BACKWARD);
}
}

Output text from the console:

Warrior 1 goes left
Warrior 2 goes left
Mage goes left
Thief goes left

Warrior 2 goes right

Mage goes forward

Mage goes backward
Thief goes backward

Literature sources:
[1] Eric Freeman, Elisabeth Freeman, Kathy Sierra, Bert Bates – “Head First Design Patterns”.

Leave a comment

Your email address will not be published. Required fields are marked *