Observer Pattern

 

Definition

The Observer Pattern defines a one-to-many dependency between objects so that when one object changes state, all of its dependents are notified and updated automatically.

image

Figure 01. Observer Pattern class diagram. Ref: http://en.wikipedia.org/wiki/Observer_pattern

Scenario

We need to simulate a Horse Race, on which we need to keep the information updated in a website once all the results of the horse race are known and also we need to update a Data Base to keep history of the result of the race.

Class Diagram

image

Figure 02. Class diagram of the case of study

As it can be seen in the previous figure, the Observer Pattern was implemented to keep the WebsiteUpdater, RaceDataBaseUpdater and ComandLineRaceDisplayUpdater updated when the race is over, as it was part of the requirement.

The Subject is the Race class, because this class is the one that is keeping record of all the horses that reach the Final line and is the one that is going to accept any Observer that want to register themselves and keep them updated.

Subject.java

package chris.desingpatterns.observer.subject;

import chris.desingpatterns.observer.observers.Observer;

public interface Subject {
    
    void registerObserver(Observer o);
    void removeObserver(Observer o);
    void notifyObservers();

}

Race.java

package chris.desingpatterns.observer.subject;

import java.util.ArrayList;
import java.util.List;

import chris.desingpatterns.observer.competitor.Raceable;
import chris.desingpatterns.observer.observers.Observer;

public class Race implements Subject {
    
    private int numberOfLaps;
    private int circuitPerimeter;
    private List<Raceable> competitors;
    private List<Raceable> raceRegister;

    //Here will be registered all the classes that want to get updates from Race
    private List<Observer> observers;
    
    public Race(int numberOfLaps, int circuitPerimeter) {
        this.numberOfLaps = numberOfLaps;
        this.circuitPerimeter = circuitPerimeter;
        raceRegister = new ArrayList<Raceable>();
        competitors = new ArrayList<Raceable>();
        observers = new ArrayList<Observer>();
    }
   
    public void registerObserver(Observer o) {
        observers.add(o);
    }

    public void removeObserver(Observer o) {
        observers.remove(o);
    }
    
    /**
     * This method is the one that will 
     * notify all the observer that the race
     * was over, so they can proceed to do 
     * whatever they need with the information
     * provided.
     */
    public void notifyObservers() {
        for(Observer o : observers){
            o.update(raceRegister);
        }
    }
    
    /**
     * Method that will wait to all the 
     * threads to get ready to run();
     * @throws InterruptedException
     */
    public synchronized void getReady() throws InterruptedException{
        this.wait();
    }
    
    /**
     * Method that will be triggered when all the
     * threads are ready to run()
     */
    public synchronized void startRace(){
        this.notifyAll();
    }
    
    /**
     * Method that will register all the objects
     * that have finished the competition.
     * @param raceable
     */
    public void reachedFinal (Raceable raceable){
        raceRegister.add(raceable);
        if(isRaceOver()){
            //Notify to update
            notifyObservers();
        }
    }
    
    public void setCompetitors(Raceable competitor){
        competitors.add(competitor);
    }
    
    /**
     * Method that will verify if all the 
     * competitors have cross the Final.
     * @return
     */
    private boolean isRaceOver(){
        return raceRegister.size() == competitors.size();
    }
    
    public int getNumberOfLaps() {
        return numberOfLaps;
    }

    public int getCircuitPerimeter() {
        return circuitPerimeter;
    }

}

The Observers are the ones that will be notified once the race is over, this will happen when the Subject class will execute the method notifyObservers() triggering the method update() in all the Observers.

Observer.java

package chris.desingpatterns.observer.observers;

import java.util.List;

import chris.desingpatterns.observer.competitor.Raceable;

public interface Observer {
    
    /**
     * This method will be triggered once our Race subject
     * has registered all the elements that were racing,
     * so by this way we can get the information about
     * positions and results of the race.
     * 
     * @param raceRegister
     */
    void update(List<Raceable> raceRegister);

}

CommandLineRaceDisplayUpdater.java

package chris.desingpatterns.observer.observers;

import java.util.List;

import chris.desingpatterns.observer.competitor.Raceable;
import chris.desingpatterns.observer.subject.Subject;

public class CommandLineRaceDisplayUpdater implements Observer {
    
    public CommandLineRaceDisplayUpdater(Subject race) {
        race.registerObserver(this);
    }
    
    /**
     * This method will display the results of the race.
     * 
     * @param raceRegister
     */
    public void display(List<Raceable> raceRegister){
        System.out.println("::::COMMAND LINE RACE DISPLAYER:::");
        System.out.println("The race is over!!!");
        
        System.out.println("The winner horse was: " 
        + raceRegister.get(0).getIdentification());
        
        System.out.println("These are the results: ");
        int i = 1;
        for(Raceable racingObject : raceRegister){
            System.out.println(i + "   " + racingObject.getIdentification() + "");
            
            System.out.println("    AVERAGE SPEED(Km/Hr): " 
                    + racingObject.getSpeed() 
                    + "| TOTAL TIME: " + racingObject.getTimeScored());
            
            i++;
        }
        System.out.println("Hope that you chose the winner horse :D!");
        System.out.println("::::::::::::::::::::::::::::::::::::");
    } 
        
    /**
     * This method will be triggered when the competition has 
     * finished.
     */
    public void update(List<Raceable> raceRegister) {
        display(raceRegister);
    }

}

And the class that will run the case of study:

Main.java

package chris.desingpatterns.observer;

import chris.desingpatterns.observer.competitor.Horse;
import chris.desingpatterns.observer.competitor.RunningObject;
import chris.desingpatterns.observer.observers.CommandLineRaceDisplayUpdater;
import chris.desingpatterns.observer.observers.RaceDataBaseUpdater;
import chris.desingpatterns.observer.observers.WebsiteUpdater;
import chris.desingpatterns.observer.subject.Race;

public class Main {

    /**
     * @param args
     * @throws InterruptedException 
     */
    public static void main(String[] args) throws InterruptedException {
        int numberOfLaps = 3;
        int circuitPerimeter = 1; // 1 Km
        Race race = new Race(numberOfLaps, circuitPerimeter);
        
        Horse horse1 = new Horse("Konan");
        race.setCompetitors(horse1);
        new Thread(new RunningObject(horse1, race)).start();
        
        Horse horse2 = new Horse("Achilles");
        race.setCompetitors(horse2);
        new Thread(new RunningObject(horse2, race)).start();
        
        Horse horse3 = new Horse("Brave Inca");
        race.setCompetitors(horse3);
        new Thread(new RunningObject(horse3, race)).start();
        
        Horse horse4 = new Horse("Black Beauty");
        race.setCompetitors(horse4);
        new Thread(new RunningObject(horse4, race)).start();
        
        Horse horse5 = new Horse("Trigger");
        race.setCompetitors(horse5);
        new Thread(new RunningObject(horse5, race)).start();
        
        //This are the objects that will update the information
        //once the race is over.
        new CommandLineRaceDisplayUpdater(race);
        new WebsiteUpdater(race);
        new RaceDataBaseUpdater(race);
        
        //Let's start the race
        System.out.println("Get ready...");
        Thread.sleep(1000);
        System.out.println("Get set...");
        Thread.sleep(1000);
        System.out.println("GO!!!");
        race.startRace();

    }

}

That is it! this design (thanks to the help of the Observable pattern) is very flexible (we can add or remove as many Observables as we want) and the Observers are not tightly coupled to the Subject.

So download the source code and enjoy the race!

Cheers!!!

Observer Pattern Code

Do you have any recommendations or amendments to be done, please let me know to improve this post!